OpenSocialを利用してガジェットを作ろう!

第6回外部サービスの利用

gooホームが正式ローンチしました!

先週の5月21日、初の国産コンシューマ向けOpenSocialコンテナとして、gooホームがリニューアルを行いました。コンセプトは「ソーシャルウェブ・ポータル⁠⁠。OpenSocialへの対応と共に、FriendFeedライクな外部サービスのインポート機能も備え、Twitterや del.icio.usなど、約30のサービスを取り込むことが出来ます。本連載筆者のひとりである北村英志氏が担当したサービスであることから、⁠はてブチェッカー」ガジェットもリニューアル当初からガジェットギャラリーに掲載されています。

OpenSocialへの対応の幅も広く、ドキュメントの充実度も合わせて、非常に注目すべきSNSです。ぜひこの機会にgooホームをお試しください。

今回のテーマは「マッシュアップ」

本連載の題材として取り上げている「はてブチェッカーガジェット」は、gooホームやmixiのユーザ情報やソーシャルグラフといった情報だけでなく、 SNSの外部にある「はてなブックマーク」というサービスの情報を取り込んで表示しています。近年のWebの世界では、多くのWebサービスを利用して一つの新しいサービスを仕立てる、つまり「マッシュアップ」が盛んに行われています。はてブチェッカーガジェットが示しているように、OpenSocial はマッシュアッププラットフォームとしても最適な環境です。

今回は、外部サービスにアクセスを行うための手順を解説し、ガジェットを更にリッチにするための手法を手に入れることにしましょう。

外部サービスへのアクセスとは

OpenSocialガジェットにおいて、外部サービスを利用する動機として、以下のような目的が考えられます。

  • 外部サービスから得られるコンテンツを利用したい。
  • 外部サービスとして設置してあるデータベースを使って情報を記録しておきたい。

マッシュアップというキーワードに関しては、前者となることでしょう。インターネット上に数多く公開されているWebサービスのほぼ全てが対象となります。扱われるコンテンツは、飲食店や書籍といった情報など、最近ではあらゆるコンテンツを扱うことができるようになってきました。その形態も、文字情報の他に、画像や動画なども利用可能ですので、⁠どのサービスを組み合わせるか」といったセンスがポイントとなるでしょう。

それに対して後者の場合は、ユーザがガジェットを使って多くの情報を共有するためのバックエンドサーバとして外部サービスを利用する目的となります。これには、Google AppEngineやAmazon EC2など、俗に言うクラウドサービスを利用することによって、ガジェットのユーザ数の増加や利用状況の増加に伴う情報量やネットワーク転送量の増大に対応することができるようになるでしょう。もちろん、任意のWebアプリケーションを任意のサーバにて運用し、それをガジェットのバックエンドサーバとして利用することも選択できます。

このどちらも場合も、OpenSocialが想定する外部サービスは、基本的にRESTによるインタフェースを備えたWebサービスが対象となります。つまり、外部サービスへのアクセスには、現在のWeb標準技術の組み合わせが採用されるということです。特に OpenSocialでは、外部サービスへのアクセスは主に以下の構成要素で成り立っています。

  • リクエスト時
    • URLによって外部サービスへのアクセス先を特定する。
    • HTTP Methodによって外部サービスへの処理種別を特定する。
    • URLパラメータやリクエストボディを使って、送信したい情報を記述する。
    • リクエストヘッダによって付加的な情報を追加する。
  • レスポンス時
    • HTTPステータスコードで外部サービスの処理結果を判断する。
    • レスポンスボディにて外部サービスからコンテンツを得る。

外部サービスにアクセスする際には、上記の内容について、通信相手の外部サービスに合わせて指定することが求められます。

OpenSocialコンテナによるプロキシ

さて、ここでガジェットから外部サービスにアクセスするための手法を考えてみましょう。ガジェットは、Webブラウザ上で動作する HTML+JavaScriptアプリケーションですので、サーバとの通信にはXMLHttpRequestを使った非同期通信が主に使われることになります。

ここで、XMLHttpRequestには「表示しているHTMLと同じドメインに対してしか通信することができない」という制限が問題となります。ガジェットの場合、そのガジェットをレンダリングしたOpenSocialコンテナ、つまりSNSのサーバとの通信に限定されることになります。Person&Friends APIなど、OpenSocial JavaScript APIに必要となる通信先はOpenSocialコンテナとなるため、この制限は受けません。しかし、マッシュアップやバックエンドサーバへの通信先は SNSとは異なる外部のサーバとなるために、XMLHttpRequestの制限によって直接通信することはできないことになります。

任意の外部サービスを利用可能とするために、OpenSocialではプロキシの手法が採用されています。つまり、Webブラウザ上のガジェットから直接外部サービスにアクセスするのではなく、OpenSocialコンテナが外部サービスへのアクセスを肩代わりしてくれる仕組みとなります。これによって、 XMLHttpRequestの制限を受けることなく、ガジェットは任意の外部サービスを利用することができます。

この仕組みは、以下のような利点も得ることができます。

  • 外部サービスから得られたコンテンツがOpenSocialコンテナにてキャッシュされる。
  • 署名付きリクエストやOAuthによる認証認可処理をOpenSocialコンテナに任せることができる。

gadgets.io.makeRequest()

では、実際にガジェットから外部サービスにアクセスするための具体的な方法を解説します。まずは、リスト1をご覧ください。

リスト1 はてなブックマークのフィード取得
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.FEED;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.NONE;
params[gadgets.io.RequestParameters.GET_SUMMARIES] = true;
params[gadgets.io.RequestParameters.NUM_ENTRIES] = 10;
gadgets.io.makeRequest("http://b.hatena.ne.jp/hotentry.rss", function(response) {
  if (response.data) {
    for (var key in response.data.Entry) {
      var entry = response.data.Entry[key];
      var title = entry.Title;
      var link = entry.Link;
      var date = entry.Date;
      var comment = entry.Summary;
      // do something..
    }
  } else {
    var err = response.errors[0];
    // do something..
  }
}, params);

外部サービスへのアクセスはgadgets.io.makeRequest()関数を使うことになります。paramsオブジェクトに対するパラメータ値のセットの行が目立ちますが、基本的にはmakeRequest()関数の引数に外部サービスのURLを指定して実行するだけで、OpenSocialコンテナが代理で外部サービスへのアクセスを行い、結果をコールバック関数に渡してくれます。とてもシンプルです。

リクエスト

makeRequest() 関数を使った外部サービスへのアクセスの際に、多くのパラメータ値を調整することによって、リクエストの内容を細かく指定することが可能です。各パラメータの項目は、gadgets.io.RequestParametersのフィールドとして定義されています。表1は、指定可能なパラメータの一覧です。

表1 リクエスト時に指定可能なフィールド
フィールド名説明初期値
AUTHORIZATIONコンテンツを取得する時に使用する認証方式を指定します。NONE
CONTENT_TYPE取得されるコンテンツの期待する種別を指定します。TEXT
GET_SUMMARIESコンテンツがフィードだった場合に、Summaryを結果に含めるかどうかを指定します。false
HEADERS外部サービスへのアクセス時に送られるリクエストヘッダを指定します。null
METHODコンテンツを取得する際に使用するHTTP Methodを指定します。GET
NUM_ENTRIESコンテンツがフィードだった場合に、取得するエントリ数を指定します。3
POST_DATAPOSTメソッド時の送信するデータを指定します。null
REFRESH_INTERVAL取得したコンテンツのキャッシュ有効時間を指定します。3600

makeRequest() 関数には、通常の外部サービスへのリクエストの他に、署名を付与することでリクエストの妥当性を確認することができる署名付きリクエストにも対応しています。これを行うためには、AUTHORIZATIONフィールド値として、gadgets.io.AuthorizationType.SIGNEDを指定します。

CONTENT_TYPEフィールド値は、外部サービスから返却されるコンテンツの期待する表現形式を指定します。 OpenSocialでは、以下の形式がサポートされています。これらの種別を指定することで、OpenSocialコンテナが自動的に結果のコンテンツを扱いやすい形式に変換してくれます。

  • Atom Feed文書やRSS文書: gadgets.io.ContentType.FEED
  • XML文書: gadgets.io.ContentType.DOM
  • JSON形式: gadgets.io.ContentType.JSON
  • テキスト形式: gadgets.io.ContentType.TEXT

特にフィード(Atom Feed文書やRSS文書)を取得する場合に、その全てを扱ってしまうと情報量がとても多いケースについて、ネットワークの負荷を増加させてしまう可能性があります。そのため、GET_SUMMARIESフィールド値としてfalseを指定することで、例えばRSS文書の場合は、description要素の文字列(つまりエントリの説明文書)を対象外とすることが可能です。

また、フィードを取得する場合には、そのエントリ数も問題となります。あまりにも多いエントリ数を一度に取得してしまうと、やはりネットワークの負荷などを招いてしまうことでしょう。NUM_ENTRIESフィールド値によって、1度のリクエストで取得するエントリ数の上限を指定することによって、この問題を解決することができます。

makeRequest()関数による外部サービスへのアクセスはRESTを想定しているため、HTTP Methodが非常に重要となります。METHODによって、以下のHTTP Methodを指定することになります。GETあるいはPOSTが最も良く使われます。

  • GET: gadgets.io.MethodType.GET
  • POST: gadgets.io.MethodType.POST
  • PUT: gadgets.io.MethodType.PUT
  • DELETE: gadgets.io.MethodType.DELETE
  • HEAD: gadgets.io.MetodType.HEAD

はてブチェッカーガジェットでは、はてなブックマークから得られるコンテンツに関して、OpenSocialコンテナのキャッシュ機構によってコンテンツが最新の結果にならない、といった状況が起きる可能性があります。これを抑止するために、REFRESH_INTERVALフィールドを使うことができます。このフィールド値として0を指定しておくことにより、キャッシュされることを抑止することが可能です。

レスポンス

makeRequest()関数によるリクエストの結果は、第2引数に渡すコールバック関数の引数に格納されています。この引数からコンテンツを取り出して、ガジェット内で利用することになります。

ただし、その前にリクエストが正しく得られたかどうかを確認することが必要です。エラー処理を抜粋したコードをリスト2に示します。

リスト2 makeRequest()関数のエラー処理
gadgets.io.makeRequest("[URL]", function(response) {
  if (response.data) {
    // do something..
  } else {
    var err = response.errors[0];
    _self.msg.createTimerMessage(err, 3);
  }
}, params);

レスポンスが得られない原因は様々ですが、どの場合もコールバック関数の引数に渡されるdataプロパティの値がセットされていない状態となります。if文にてresponse.data値がundefinedかどうかを調べることで、エラーが発生したかどうかを確認することができます。この際、同時に errorsプロパティにエラーメッセージなどの情報が格納されますので、リスト2ではその一つ目の要素を取り出してメッセージ表示しています。

さて、正常にコンテンツを得られた場合、コールバック関数に渡される引数のプロパティを使って結果を得ることができます。その結果は、以下の2つの方法で得られます。

  • textプロパティ: 結果のコンテンツ文字列をそのまま取得する。
  • dataプロパティ: 結果のコンテンツをJavaScriptオブジェクト形式で取得する。

OpenSocial コンテナは、リクエスト時にCONTENT_TYPEフィールド値で指定した期待する種別に基づいて、自動的にJavaScriptオブジェクトに変換処理を行います。そのため、多くの場合は後者のdataプロパティを使ってコンテンツを得ると良いでしょう。

はてブチェッカーガジェットの場合、得られるコンテンツはRSS文書の形式となりますので、リスト1ではContentType.FEEDを指定しています。 makeRequest()関数の呼び出し結果として、はてなブックマークサービスのサーバからは、リスト3のような結果を得ることになるでしょう。

リスト3 はてなブックマークサービスからの結果
<rdf:RDF ...>
  <item ...>
    <title>[タイトル文字列]</title>
    <link>[エントリのURL]</link>
    <description>[エントリのサマリー] or [ブクマコメント]</description>
  </item>
  ...
</rdf:RDF>

この結果を自分で文字列解析またはDOM解析するのは、できればやりたくない作業です。OpenSocialコンテナは、リスト3の結果を自動的に JavaScriptオブジェクトの形式に変換してくれます。リスト4は、リスト3の変換結果が、ContentType.FEEDの指定によって変換された結果をJSON形式で表したものです。

リスト4 変換結果
{
  Entry: [
    {
      Title: [タイトル文字列],
      Link: [エントリのURL],
      Data: [エントリの更新日付]
      Summary: [エントリのサマリー] or [ブクマコメント]
    },
    ...
  ]
}

はてブチェッカーガジェットでは、makeRequest()関数によって取得したはてなブックマークサービスからの結果について、リスト5のようにしてレンダリングしています。このリスト内でエントリごとに動的に変更される部分については、以下のようになります。

  • ${Title} - response.data.Entry[n].Title
  • ${Link} - response.data.Entry[n].Link
  • ${EntryLink} - "http://b.hatena.ne.jp/entry/" + response.data.Entry[n].Link
  • ${EntryImage} - "http://b.hatena.ne.jp/entry/image/" + response.data.Entry[n].Link
リスト5 はてなブックマークのレンダリング
<div class="summary">
    <a href="${Link}">[タイトル文字列]</a>
    <a href="${EntryLink}">
        <img src="${EntryImage}" />
    </a>
</div>
<div class="coment">${Summary}</div>

リスト1のfor文の中で、上記のように各エントリの情報を取得し、リスト5のようにレンダリングしています。

まとめ

今回は、gadgets.io.makeRequest()関数を使って、外部サービスへのアクセスと、取得したコンテンツの処理方法について解説しました。OpenSocialガジェットにおいて、このmakeRequest()関数は非常に多く使われるとても重要な関数ですので、ぜひとも自分のものにしてください。

次回からは、OpenSocialでは欠かすことのできないパーミッションモデルやアクティビティに関する解説をしていく予定です。お楽しみに!

おすすめ記事

記事・ニュース一覧