本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

REST サービスに Dojo の JsonRestStore を使う

単純で汎用的なサービスの実装方法を学ぶ

Nick Maynard, Senior Software Engineer, IBM
Author photo
Nick Maynard はイギリスの Hursley にある IBM Software Solutions Transformation チームで働いています。彼の専門は、Web プログラミング、Linux、Web サービス、ビジネス統合技術などです。彼の連絡先は nick.maynard@uk.ibm.com です。

概要: Dojo の JsonRestStore は Dojo のデータ API (dojo.data) に REST サービスを接続するためのより高度な方法の 1 つです。サービスのデータ構造が JsonRestStore で想定されるデータ構造に一致していない場合、JsonRestStore と REST サービスとの接続が難しくなります。この記事では、標準的ではない REST サービスを JsonRestStore に接続するための、単純で汎用的なサービスを実装する方法について説明します。いくつかの例をとおして、この実装を皆さん独自のサービスに対して使用する方法と拡張する方法を学びましょう。

日付:  2010年 12月 14日
レベル:  中級 この記事の原文:  英語
アクティビティー: 4010 ビュー
お気軽にご意見・ご感想をお寄せください: 


はじめに

Dojo のデータ抽象化レイヤーである dojo.data は、Dojo アプリケーションから多様なバックエンド・サービスにアクセスするための標準的な API です。Dojo の拡張機能ライブラリー DojoX の中にある JsonRestStore を使用すると、バックエンドの REST (Representational State Transfer) サービスにアプリケーションを簡単に接続することができます。

JsonRestStore は Dojo を REST に接続するための適切なソリューションですが、JsonRestStore では、サービスに対して渡す情報、またサービスから返される情報に対するデフォルトの想定フォーマットは決まっています。これらのやり取りをカスタマイズするための SMD (Service Mapping Description) 機能は、決して理解しやすいものではありません。

JsonRestStore は、読み取り、書き込み、通知のためのフル機能を備えたデータ・ストアであり、HTTP/REST 標準をベースに、GET、PUT、POST、DELETE コマンドを使ってサーバーとやり取りすることができます。JsonRestStore により、Dojo Data API と JavaScript を使ってサーバー・サイドのデータベースや永続データ・ストレージと通信することができ、また効率的に CRUD (Create、Read、Update、Delete) 操作をすることができます。

Dojo アプリケーションを作成する際に既存のバックエンド REST ストアと通信する必要がある場合、あるいはデータ・ストアの通信を変更することができない場合には、カスタムの dojox.rpc.Service 実装を使います。

この記事では、標準的ではない REST サービスを JsonRestStore に接続するための、単純で汎用的なサービスを実装する方法について学びます。実例を使って手順を示しながら、この実装を皆さんが提供しているサービスに対して使用する方法と拡張する方法について説明します。

この記事のソース・コードをダウンロードする方法については、「ダウンロード」セクションを参照してください。


サービスの実装の例

EasyRestService には、4 つの REST 操作 (POST (作成)、GET (読み取り)、PUT (更新)、DELETE (削除)) に対する実装が用意されており、それぞれのアクティビティーごとに、サービス・メソッドの呼び出しの前後にフックできる機能があります。呼び出しのライフサイクルは以下のとおりです。

  1. Dojo で XHR を呼び出すための標準的な構造を作成します (この構造には、URL、引数、データ・フォーマットのメタデータが含まれています)。
  2. メソッドの引数を変換する関数を呼び出します。このメソッドによって呼び出しの構造を (パスを含めて) 変更し、またこのメソッドを使用することで、呼び出しの構造を REST サービスで想定される構造と同じになるように変換します。
  3. コンテンツ・ノードを JSON 表現に変換することにより、呼び出しの構造の putData/postData/deleteData/getData ノードにデータを追加します。
  4. XHR を呼び出すための変更された構造を使って XHR メソッドを呼び出します。
  5. XHR コールバックに呼び出し結果の変換関数を追加します。このメソッドにより、呼び出し結果のデータ構造を変更することができます。このメソッドを使用して、JsonRestStore で想定される構造に結果を変換します。

リスト 1 に EasyRestService のコードを示します。


リスト 1. EasyRestService のソース・コード

dojo.provide("com.ibm.developerworks.EasyRestService");

(function() {
  var pa = com.ibm.developerworks.EasyRestService = function (path, serviceImpl, schema) {
    // Enforce the dojox.rpc.Rest trailing slash functionality
    path = path.match(/\/$/) ? path : (path + '/');
    
    // A dojox.rpc.Service implementation is a function with 3 function members
    var service;
    // GET function
    service = function(id, args) {
      return _execXhr("get", id, args);
    };
    // POST function member
    service['post'] = function(id, value) {
      return _execXhr("post", id, value);
    };
    // PUT function member
    service['put'] = function(id, value) {
      return _execXhr("put", id, value);
    };
    // DELETE function member
    service['delete'] = function(id) {
      return _execXhr("delete", id);
    };
    
    // Generic XHR function for all methods
    var _execXhr = function(method, id, content) {
      // Transform the method string
      var methodCapitalised = method.substring(0,1).toUpperCase() 
        + method.substring(1).toLowerCase();
      var methodUpperCase = method.toUpperCase();
      var methodLowerCase = method.toLowerCase();
      
      // Get the transformer functions
      var argumentsTransformer = service["transform" + methodCapitalised + "Arguments"];
      var resultTransformer = service["transform" + methodCapitalised + "Results"];
      
      // Construct the standard query
      var serviceArgs = {
        url : path + (dojo.isObject(id) ? '?' + dojo.objectToQuery(id) : 
          (id == null ? "" : id)), 
        handleAs : "json",
        contentType : "application/json",
        sync : false,
        headers : { Accept : "application/json,application/javascript" }
      };
      
      // Transform the arguments
      // NOTE: argumentsTransformer has a reference to "service"
      serviceArgs = argumentsTransformer(serviceArgs, arguments);

      // Copy the content into the appropriate *Data arg
      // getData, putData, postData, deleteData
      // NOTE: If you want your arguments transformer to edit the *Data arg directly, 
      // move the arguments transformer invocation to after this call 
      serviceArgs[methodLowerCase + 'Data'] = content;
            
      // Kick off the call
      var xhrFunction = dojo['xhr' + methodCapitalised];
      var deferred = xhrFunction(serviceArgs);
      // Add our result transformer
      // NOTE: resultTransformer has a reference to "service" too
      deferred.addCallback(dojo.partial(resultTransformer, deferred));
      
      return deferred;
    };

    // Mix in the service hooks
    // Uses a "default" implementation that does nothing
    // Service hooks will have a reference to the "service" object in their context
    dojo.mixin(service, 
      new com.ibm.developerworks.EasyRestService.DefaultHooks(), 
      serviceImpl);
    
    // Now remove any default _constructor() methods
    // This is necessary as the JsonRestStore stack uses _constructor() differently
    delete service['_constructor'];
    // Remove the declaredClass member if it has been added
    delete service['declaredClass'];
    
    // Save the path away
    service.servicePath = path;
    // Save the schema
    service._schema = schema;
    
    return service;
  };
})();

dojo.declare("com.ibm.developerworks.EasyRestService.DefaultHooks", null, {
  transformGetArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformPutArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformPostArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformDeleteArguments: function(serviceArgs) {
    // Alter serviceArgs to provide the information the backend
    // service requires
    return serviceArgs;
  },
  transformGetResults: function(deferred, results) {
    /*
     * JsonRestStore expects the following format:
     * [
     *  { id: "1", ... },
     *  { id: "2", ... },
     *  ...
     * ] 
     */
    return results;
  },
  transformPutResults: function(deferred, results) {
    /*
     * JsonRestStore does not expect any specific content here
     */
    return results;
  },
  transformPostResults: function(deferred, results) {
    /*
     * JsonRestStore expects:
     * 1) A "Location" response header with location of the new item.
     * 		From the Dojo API:
     * 			The server’s response includes a Location header
     * 			that indicates the id of the newly created object.
     * 			This id will be used for subsequent PUT and DELETE 
     * 			requests. JsonRestStore also includes a 
     * 			Content-Location header that indicates the temporary
     * 			randomly generated id used by client, and this 
     * 			location is used for subsequent PUT/DELETEs if no 
     * 			Location header is provided by the server or if 
     * 			a modification is sent prior to receiving a response 
     * 			from the server.
     *    NB: There is no JS method for altering response headers.  
     *      You might wish to try overriding the 
     *      deferred.ioArgs.xhr.getResponseHeader() method with your
     *      own implementation.
     * 2) The new item in the following format:
     * { id: "1", ... }
     */
    return results;
  },
  transformDeleteResults: function(deferred, results) {
    /*
     * JsonRestStore does not expect any specific content here
     */
    return results;
  }
});

リスト 1 のコードは dojox.rpc.Rest のデフォルト関数をコピーしたものです。このコードを JsonRestStore に使用することができます (リスト 2)。


リスト 2. デフォルトで JsonRestStore に EasyRestService を使用する

dojo.require("com.ibm.developerworks.EasyRestService");
dojo.require("dojox.data.JsonRestStore");

var store = new dojox.data.JsonRestStore({
  service: new com.ibm.developerworks.EasyRestService("https://mydomain.com/restservice"),
  idAttribute : "id"
});

デフォルトで、EasyRestService インスタンスは呼び出しの引数と、呼び出し結果をまったく変更しません。EasyRestService は、引数と結果に対する必要な変換を行うために DefaultHooks を変更するのではなく、インスタンス単位でこれらの変換処理を変更するためのメカニズムを提供します。


EasyRestService をカスタマイズする

EasyRestService にはカスタム変換のための単純なメカニズムがあります。リスト 3 の例では、EasyRestService の動作を変更し、実行前に GET の呼び出しの構造をログに記録するようにしています。


リスト 3. EasyRestService をカスタマイズする

dojo.require("com.ibm.developerworks.EasyRestService");

var transformers = { 
  transformGetArguments: function(args) { 
    console.log(args); 
    return args; 
  }
};
var service = new com.ibm.developerworks.EasyRestService(
  "https://mydomain.com/restservice", transformers);

同様に、DefaultHooks での変換処理はすべて、インスタンス単位で変更することができます。

ここでは EasyRestService の 2 つのインスタンスを作成します。1 つのインスタンスは REST に準拠したサービスであり、もう 1 つは REST に準拠していないサービスです。この例では、この 2 つのインスタンスを JsonRestStore の 2 つのインスタンスに対するサービス・プロバイダーとして使用し、データ・ストアに対する基本的なフェッチを実行します。

データ・ストア

リスト 4リスト 5 では、JSON 構造を含む 2 つの読み取り専用ファイルを使ってサービスを作成しています。


リスト 4. REST に準拠するサービス、compliantService.json

[
	{ id: 1, name: "Phil" },
	{ id: 2, name: "John" }
]


リスト 5. REST に準拠しないサービス、noncompliantService.json

{ items : [
	{ id: 1, name: "Phil" },
	{ id: 2, name: "John" }
] }

データ・ストアとサービスとの間でやり取りを行うコード

リスト 6 の JavaScript コードにより、データ・ストアをインスタンス化し、データ・ストアに対してクエリーを実行します。


リスト 6. データ・ストアとやり取りするための JavaScript コード


// Create a store using a service that needs no transformations
compliantStore = new dojox.data.JsonRestStore({
  service : new com.ibm.developerworks.EasyRestService(
      "./compliantService.json"),
  idAttribute : "id"
});

// Cause an async fetch from the compliant service
dojo.create("p", {
  innerHTML : "Requesting from compliant service"
}, dojo.body(), "last");
compliantStore.fetch({
  onComplete : function(items, request) {
    console.log(items);
    // Log the number of items fetched
    dojo.create("p", {
      innerHTML : "Got " + items.length + " items from compliant service."
    }, dojo.body(), "last");
  }
});

// Create a store using a service which needs transformations
// to interpret the results
noncompliantStore = new dojox.data.JsonRestStore({
  service : new com.ibm.developerworks.EasyRestService(
      "./noncompliantService.json", {
        transformGetResults : function(deferred, results) {
          // This store wraps its results in an items object
          // so return the items object
          return results.items;
        }
      }),
  idAttribute : "id"
});

// Cause an async fetch from the noncompliant service
dojo.create("p", {
  innerHTML : "Requesting from noncompliant service"
}, dojo.body(), "last");
noncompliantStore.fetch({
  onComplete : function(items, request) {
    console.log(items);
 	// Log the number of items fetched
    dojo.create("p", {
      innerHTML : "Got " + items.length
          + " items from noncompliant service."
    }, dojo.body(), "last");
  }
});


まとめ

この記事では、独自の REST サービスを JsonRestStore に接続する方法を学びました。取り上げた例では、JsonRestStore で要求されるシグニチャーを提供するためにサービスのインターフェースを変換する単純な方法を示しました。JsonRestStore で想定されるデータ構造についての完全な説明は、DefaultHooks についてのコメント、DojoCampus に用意された JsonRestStore のドキュメント、API のドキュメントなどを参照してください。

JsonRestStore で想定されるデータ構造についての完全な説明は、「参考文献」セクションを参照してください。



ダウンロード

内容ファイル名サイズダウンロード形式
EasyRestService code sample - project archiveEasyRestServiceExample.zip6.5KBHTTP

ダウンロード形式について


参考文献

学ぶために

製品や技術を入手するために

議論するために

著者について

Author photo

Nick Maynard はイギリスの Hursley にある IBM Software Solutions Transformation チームで働いています。彼の専門は、Web プログラミング、Linux、Web サービス、ビジネス統合技術などです。彼の連絡先は nick.maynard@uk.ibm.com です。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Java technology
ArticleID=619313
ArticleTitle=REST サービスに Dojo の JsonRestStore を使う
publish-date=12142010
author1-email=nick.maynard@uk.ibm.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。