Dojo的数据抽象层dojo.data,为Dojo提供一个标准API来访问各种后端服务。JsonRestStore,位于Dojo扩展库DojoX之中,允许您将您的应用程序快速连接到后端具象状态传输(Representational State Transfer,REST服务。
JsonRestStore是链接Dojo到REST服务的一个优秀的解决方案。不过在默认情况下,JsonRestStore对传递给服务和由服务返回的信息拥有预期的格式。用于定制这些交互的Service Mapping Description (SMD)设备功能有限,且使用方法比较难以理解。
JsonRestStore是一个数据存储,使用GET、PUT、POST和DELETE命令,通过基于与服务器交互的HTTP/REST标准提供完整的读取、写入和通知功能。它允许您使用Dojo Data API和JavaScript与服务器端数据库/持久性数据存储通信,以及高效地处理创建、读取、更新和删除(CRUD)操作。 当Dojo应用程序创建者想要与一个预先存在的后端REST存储通信时,如果储存的通信方式不能改变,它们使用一个自定义的dojox.rpc.Service实现。
在本文中,学习一个简单、通用的服务实现,用于将非标准REST服务连接到JsonRestStore。通过一个实际示例研究如何使用和扩展它用于您自己的服务。
服务实现示例
EasyRestService提供4个REST操作的实现:POST (create)、GET (read)、PUT (update)和DELETE (delete)。对于每个活动,它在服务方法调用之前和之后提供hook能力。调用生命周期是:
构建一个含有URL、参数和数据格式元数据的标准Dojo XHR调用结构。
调用该方法的参数转换函数。该方法可以改变调用结构(包括路径),且可用于转换调用结构,以符合REST服务期望。
通过将内容节点转换为一个JSON形式,填入调用结构的putData/postData/deleteData/getData节点。
使用改变的XHR调用结构调用XHR方法。
添加一个结果转换器到XHR回调函数。这个方法可以改变结果的数据结构。使用它可以将结果转换成JsonRestStore预期的结构。
清单1显示了示例代码。
清单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. 默认情况下,EasyRestService和JsonRestStore的使用
以下是引用片段: 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实例不能以任何方式改变参数和结果。相反可以改变DefaultHooks来执行所需的参数和结果转换,EasyRestService根据每个实例(per-instance)基础提供一个机制来重写这些转换函数。
自定义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示例:一个是针对遵从服务的,另一个是针对非遵从服务的。本例使用它们作为JsonRestStore这两个实例的服务提供者,并根据存储获取基础提取。
数据存储
在清单4和清单5中,使用两个含有JSON结构的只读文件对服务进行模拟。
清单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” } ] } |
存储和服务交互代码
JavaScript将使用清单6中的代码实例化和查询存储。
清单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文档。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
作者
相关推荐
-
企业架构 请用好移动设施和云计算
虽然很多企业都实施了移动化,但是并没有改变其底层架构。其结果就是,他们最终会围绕手机这样一个集成点来开发一个轴辐型的架构。
-
手把手教你用云服务构建移动应用(上)
你一定听过平台即服务(PaaS)或者是基于云的服务,如Google Apps Engine、CloudBees、Heruko、Engine Yard和Cloud Foundry。
-
JQuery领军Ajax风格开源框架开发
近几年,jQuery开源库和框架在在像Dojo、Prototype和GWT等的众多框架中获得了更多的青睐。尽管大众认为其在部分开发中占据优势,但jQuery仍旧被遗忘……
-
如何结合Dojo和JAX-RS创建RESTful服务?
除了广泛的用户界面小部件外,Dojo还提供了丰富的数据服务API,可轻松地连接服务器端Web服务。那么如何结合Dojo和JAX-RS创建RESTful服务呢?