使ってみよう! Windows Live SDK/API

第18回Windows Live Application Based Storage API(3)

本記事の対象APIは既にサポートされていません。記事は参考程度にご利用ください。

クライアントでAPI操作

前回はWindows Live Application Based Storage APIを利用したWebアプリケーションを作成しました。ASP.NETを使用し、APIによるユーザーデータ扱う処理のほとんどはWebアプリケーションをホストしているサーバ側で処理していました。

今回はMicrosoft Inter-frame Messaging XMLHttp APIを利用して、ユーザーデータの操作処理の多くをWebブラウザで行う方法を紹介します。APIを利用すると、クライアントとWindows Live サービスのサーバ間の通信となり、Webアプリケーションサイトへの負荷を軽減できます。

Microsoft Inter-frame Messaging XMLHttp API

Microsoft Inter-frame Messaging (IFM) XMLHttp APIはHTTPによりサーバとデータを非同期で送受信するためのJavaScriptライブラリです。Application Based Storage APIによるユーザーデータの操作に特化したライブラリではありませんが、少なくとも組み合わせた利用が想定されています。

これまで利用していたApplication Based Storage ATOM APIはHTTPによるXMLベースのプロトコルを使用しています。そのためユーザーデータへのアクセスにこのIFM XMLHttp APIが利用でき、両APIを組み合わせて利用することで多くの処理をクライアント側で完結できます。

ただし、すべてをクライアント側で処理することはできません。Windows Live委任認証を利用するにPOSTデータを承認要求ページから受け取り処理する必要がありますが、JavaScriptだけでは不可能です。ある程度Webアプリケーションのサーバでの処理も必要になります。

IFM XMLHttp APIの利用の準備

コードの参照

実際にIFM XMLHttp APIを利用してみましょう。利用するにはWebページのHTMLコードに、次のようにライブラリを参照する記述をheadタグ内に追加します。

<script type="text/javascript" src="https://controls.services.live.com/scripts/base/v0.3/ifmxmlhttp.js"></script>

ほかにもWindows Liveサービス系のライブラリを参照しておきます。後ほどのコードで使用します。

<script type ="text/javascript" src = "https://controls.services.live.com/scripts/base/v0.3/live.js"></script>

Channelファイルの作成

IFM XMLHttp APIを利用するため、Channelファイルと呼ばれるHTMLファイルを作成しWebアプリケーションサーバ上に置く必要があります。通常、ファイル名をChannel.htmとし、その内容は以下のようにJavaScriptコードを参照するようにします。

<script type="text/javascript" src="http://controls.services.live.com/scripts/base/v0.3/channelreceiver.js"></script>

リクエストの作成と送信

リクエストを送信するまでのコードを書いてみましょう。ライブラリはXMLHttpRequestというクラスのみを持っています。これを使用してサーバと通信します。インスタンスを生成するコードは次のようになります。

var xmlhttp = new Microsoft.Live.Channels.XMLHttpRequest(
    "https://cumulus.services.live.com/@xhrproxy",
    "Channel.htm",
    "https://cumulus.services.live.com/@xhrproxy?name=channel");

コンストラクタ引数は先頭から順に以下の通りです。

proxyURL

Windows Liveサービス上のXMLHttpプロキシと呼ばれるサーバへのURLになります。⁠https://cumulus.services.live.com/@xhrproxy」を指定します。

parentChannelURL

Channelファイルのパスを指定します。省略すると「Channel.htm」が指定されたことになります。

proxyChannelURL

Windows Liveサービス上のChannelファイルのURLです。⁠https://cumulus.services.live.com/@xhrproxy?name=channel」を指定します。

次にXMLHttpRequestクラスのonreadystatechangeプロパティにリクエストの応答を受け取るコールバック関数を指定します。

// (onreadystatechangeというfunctionを指定した場合)
xmlhttp.onreadystatechange = onreadystatechange;

続いてopenメソッドを呼び出し、HTTPメソッドおよびリクエストするURLを指定します。まだこの時点ではサーバにリクエストが送信されません。たとえば、次のようなコードになります。

// (lidはユーザーデータを示す識別子が格納されているものとする)
xmlhttp.open("GET", 'https://cumulus.services.live.com/@C@' + lid + '/AtomApplicationStorage/RootFolders');

上記コードに示すように、openメソッドの引数の先頭にはGETやPOSTなどのHTTPメソッドを示す文字列を指定します。続いて、リクエストするURLを指定します。

委任認証にはAuthorizationヘッダの指定が必要でした。setRequestHeaderメソッドを使用してヘッダの指定が可能です。引数にはヘッダの名前とその値を指定します。

// (deltは委任トークンが格納されているものとする)
xmlhttp.setRequestHeader('Authorization', 'DelegatedToken dt="' + delt + '"');

実際にリクエストを送信するにはsendメソッドを使用します。

xmlhttp.send(null);

sendメソッドの引数には、POSTメソッドによりデータを送信する場合にテキストまたはXML形式の文字列を指定します。

レスポンスの受信

リクエストを送信後、その応答処理はonreadystatechangeプロパティに指定した関数で行います。

レスポンス受信時に使用できるXMLHttpRequestクラスのプロパティは次の表のものがあります。

プロパティ名 説明
Readystate

リクエストの処理状況を示す数値

0: 未初期化

1: 未送信

2: 送信完了

3: 受信中

4: 受信完了

responseText

受信した内容(文字列形式)

responseXML

受信した内容(DOM形式)

status

HTTPステータスコード

statusText

HTTPステータスを示す文字列

リクエスト送信からレスポンス受信を含む、ここまでの内容をまとめたコードを示します。コード中のrequest関数を呼び出すと、Application Based Storage APIによりRootFoldersの情報をリソースプロバイダにリクエストし、リクエストが成功した場合にレスポンス内容を表示しています。

<script type="text/javascript">
    var xmlhttp = new Microsoft.Live.Channels.XMLHttpRequest(
        'https://cumulus.services.live.com/@xhrproxy',
        'Channel.htm',
        'https://cumulus.services.live.com/@xhrproxy?name=channel');

    function request() {
        var lid = Microsoft.Live.Platform.getCookie("lid");
        var delt = Microsoft.Live.Platform.getCookie("delt");

        function onreadystatechange() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                alert(xmlhttp.responseText);
           }
        }

        xmlhttp.onreadystatechange = onreadystatechange;
        xmlhttp.open("GET", 'https://cumulus.services.live.com/@C@' + lid + '/AtomApplicationStorage/RootFolders');
        xmlhttp.setRequestHeader('Authorization', 'DelegatedToken dt="' + delt + '"');
        xmlhttp.send(null);
    }
</script>

上記のコードは、Cookieにlidとdeltという名前でユーザーデータを示す識別子と委任トークンが格納されていることを前提としています。Cookieの値の取得には、Microsoft.Live.Platform.getCookieメソッドを使用しています。このメソッドはlive.jsに含まれているものです。

前回で作成したWebアプリケーションもこのようにCookieを使用していますので、前回作成したページに追記することですぐに上記コードの確認ができます。

次のように何らかの方法で作成requestメソッドを呼ぶと図1のようなウィンドウが表示される結果となります。

<input type="button" onclick="request();" value="Request" />
図1 レスポンスの表示
図1 レスポンスの表示

以上で、リクエストの作成と送信からレスポンスの受信までの一連の処理は終わりです。実は、XMLHttpRequestクラスは、多くのWebブラウザで実装されているXMLHttpRequestとほぼ同じであることがわかります。

XMLの解析

レスポンスの受信で示したコードは単に受信したXMLデータを表示しているだけでしたが、実際にはXMLの解析処理が必要になります。リソースプロバイダからフォルダ・ファイル情報一覧を受け取ったときを例にXMLを解析してみましょう。

受信完了後、XMLHttpRequest.responseXMLプロパティを参照して、Document Object Model(DOM)操作を行います。

Feed形式の場合、次のようなXMLが返ってきていました。

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" 
      xmlns:AS="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
      xmlns:AppStorage="http://dev.live.com/AppStorage" 
      xmlns:Live="LiveAtomBase:">
    ...
    <entry AS:type="Folder">
            <category scheme="http://dev.live.com/AppStorage/scheme" term="Folder" label="Folder" />
            <id>https://cumulus.services.live.com/@C@[LID]/AtomApplicationStorage/RootFolders(136)/Items('150F')</id>
            ...
            <title>Sample</title>
            ...
            <link href="https://cumulus.services.live.com/@C@[LID]/AtomApplicationStorage/RootFolders(136)/Items('150F')/Items" rel="related" type="application/atom+xml;type=feed" title="Items" />
            ...
    </entry>
    <entry AS:type="Photo">
        ...
    </entry>
    <entry AS:type="Document">
        ...
    </entry>
</feed>

ここから、<feed>要素の中にあるアイテムノードを表す<entry>要素を参照して、アイテムの名前とそのリソースパス、アイテムの種類(フォルダ・文書・画像)を取得してみましょう。

onreadystatechangeプロパティに指定するコールバック関数をひとまず次のように記述します。if文の条件式は、リクエスト処理が完了し、HTTPステータスコードが200(OK⁠⁠、responseXMLプロパティに値が格納されている場合以外のとき処理を終了するようにしています。

function onreadystatechange() {
    if (!(xmlhttp.readyState == 4 && xmlhttp.status == 200 && xmlhttp.responseXML)) {
        return;
    }
}

このif文以降にコードを追記していきます。

まず<feed>要素を示すオブジェクトを変数feedに格納します。

var responseXML = xmlhttp.responseXML;

// feed 要素の取得
var feed;
for (var i = 0; i < responseXML.childNodes.length; ++i) {
    var xml = responseXML.childNodes[i];
    if (xml.localName == "feed" || xml.baseName == "feed") {
        feed = xml.childNodes;
        break;
    }
}

<feed>要素内にある各<entry>要素について操作します。アイテムの名前は<tilte>、リソースパスは<id>要素の値を使用します。アイテムノードの種類は<entry>要素の属性値を取得し、これらを表示します。

// entry 要素の列挙
for (var i = 0; i < feed.length; ++i) {
    if (feed[i].localName != "entry" && feed[i].baseName != "entry") {
        continue;
    }

    var entry = feed[i].childNodes;
    var path;
    var title;

    // entry 要素内の title, id 要素の値取得
    for (var j = 0; j < entry.length; ++j) {
        if (entry[j].localName == "title" || entry[j].baseName == "title") {
            title = entry[j].text || entry[j].textContent;
            if (path) break;
        } else if (entry[j].localName == "id" || entry[j].baseName == "id") {
        path = entry[j].text || entry[j].textContent;
            if (title) break;
        }
    }

    // entry 要素の AS:type 属性値の取得
    var type = feed[i].getAttribute("AS:type");

    // 表示
    if (title && path && type) {
        alert(title + "\n" + path + "\n" + type);
    }
}

以上を実行すると図2のようなウィンドウが表示され、アイテムノードの情報が取得できていることがわかります。

図2 アイテムノード情報の表示
図2 アイテムノード情報の表示

POSTデータの送信

フォルダの作成

POSTデータを送信する方法も確認しておきましょう。フォルダを作成する場合、リソースパスにコレクションノードを指定して、次の<etnry>要素の文字列をPOSTデータに指定してリクエストします。

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:LivePhotos="http://dev.live.com/photos" LP:type="Folder">
    <category scheme="http://dev.live.com/AppStorage/scheme" term="Folder" label="Folder" />
    <title>[フォルダ名]</title>
</entry>

<entry>要素のPOSTデータを送信するには、Content-Typeヘッダに「application/atom+xml」を指定しておく必要があります。

xmlhttp.setRequestHeader('Content-Type', 'application/atom+xml');

そして、実際にデータを送信するにはsendメソッドの引数に<entry>要素の文字列を指定します。フォルダ作成のリクエストを送信するコードは次のようになります。

var folder = 'New Folder'; // 作成するフォルダ名
var path = 'RootFolders(124)/Items'; // 作成する場所

var post = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:LivePhotos="http://dev.live.com/photos" LP:type="Folder">' +
           '<category scheme="http://dev.live.com/AppStorage/scheme" term="Folder" label="Folder" />' +
           '<title>' + folder + '</title>' +
           '</entry>';

xmlhttp.onreadystatechange = onreadystatechange;
xmlhttp.open("POST", 'https://cumulus.services.live.com/@C@' + lid + '/AtomApplicationStorage/' + path);
xmlhttp.setRequestHeader('Authorization', 'DelegatedToken dt="' + delt + '"');
xmlhttp.setRequestHeader('Content-Type', 'application/atom+xml');
xmlhttp.send(post);

レスポンス時の処理をみてみましょう。フォルダ作成に成功した場合のHTTPステータスコードは201(Created)です。コールバック関数のコードは以下のよう書くことができます。

function onreadystatechange() {
    if (xmlhttp.readyState != 4) {
        return;
    }

    alert(xmlhttp.statusText);
    if (xmlhttp.status == 201) {
        // フォルダ作成に成功した場合 
        alert(xmlhttp.getResponseHeader('Loaction'));
    } else if (xmlhttp.status == 409) {
        // 同名のフォルダあった場合
        alert(xmlhttp.statusText);
    }
}

上記コードでは作成に成功した場合は、getResponseHeaderメソッドを使用してLocationヘッダの値を取得しています。Locationヘッダには作成されたリソースパスが格納されています。既に同名のフォルダがあった場合はHTTPステータスコード409(Conflict)が返ってきます。

ファイルのアップロード

同じPOSTメソッドを使用するファイルのアップロードについても確認しておきましょう。POSTデータにはアップロードするファイルのデータを格納してリクエスト送信します。

ただし、IFM XMLHttp APIはテキストベースの内容に限られています。Content-Typeに指定できる値は「text/plain」に制限され、テキスト形式のデータのみストレージに追加できます。

追加するファイル名の指定はslugヘッダを使用します。以上を踏まえるとファイルアップロードのコードは次のようになります。

var file = 'New File'; // 作成するファイル名
var path = 'RootFolders(124)/Items'; // 作成する場所
var post = 'Content'; // ファイルの内容

xmlhttp.onreadystatechange = onreadystatechange;
xmlhttp.open("POST", 'https://cumulus.services.live.com/@C@' + lid + '/AtomApplicationStorage/' + path);
xmlhttp.setRequestHeader('Authorization', 'DelegatedToken dt="' + delt + '"');
xmlhttp.setRequestHeader('slug', file);
xmlhttp.setRequestHeader('Content-Type','text/plain');
xmlhttp.send(post);

おわりに

いかがでしたでしょうか。前回のようにユーザーデータ操作のすべてについては触れませんでしたが、説明に示したコードを利用すれば、フォルダ名の変更(PUTメソッド⁠⁠、フォルダ・ファイルの削除(DELETEメソッド)も可能です。

以上のようにApplication Based Storage APIとIFM XMLHttp APIを使用すればほとんどの処理をJavaScriptで行うことができ、Web上にユーザー個別のデータを保存が可能になります。うまく利用することでWebアプリケーションサーバのリソースを削減し、おもしろいアプリケーションが作成できるかもしれませんね。

XML解析で示したコードは非常にわずらわしいものだったかと思います。JavaScriptでXML操作はあまり向いていません。Windows Live SDKにはこの点に関して便利なライブラリは用意されていませんので、この辺りの処理は、XML DOM形式からJSON形式に変換する、その他のライブラリと組み合わせるとよいでしょう。

おすすめ記事

記事・ニュース一覧