Dojo的数据抽象层dojo.data为Dojo应用程序提供了访问各种后端服务的标准API。 位于Dojo扩展库DojoX中的JsonRestStore使您可以快速将应用程序连接到后端表示状态传输(REST)服务。
JsonRestStore是将Dojo链接到REST服务的很好的解决方案。 但是,默认情况下,JsonRestStore对传递给服务并由服务返回的信息的格式有期望。 自定义这些交互的服务映射描述(SMD)工具充其量很难理解。
JsonRestStore是一个数据存储,通过使用GET,PUT,POST和DELETE命令与服务器进行HTTP / REST交互,通过标准提供了完整的读取,写入和通知功能。 它使您可以使用带有JavaScript的Dojo Data API与服务器端数据库/永久数据存储进行通信,并有效地处理创建,读取,更新和删除(CRUD)操作。
当Dojo应用程序作者需要与预先存在的后端REST存储进行通信时,当该存储的通信无法更改时,他们将使用自定义dojox.rpc.Service实现。
在本文中,学习一种简单的通用服务实现,用于将非标准REST服务连接到JsonRestStore。 通过一个实际的例子,探索如何使用它并将其扩展到自己的服务中。
要下载本文的源代码,请参阅“ 下载”部分。
服务实施示例
EasyRestService提供了四个REST操作的实现:POST(创建),GET(读取),PUT(更新)和DELETE(删除)。 对于每个活动,它都提供了在调用服务方法之前和之后挂接的功能。 调用生命周期为:
构造一个包含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实例不会以任何方式更改参数和结果。 EasyRestService无需更改DefaultHooks即可执行所需的参数和结果转换, EasyRestService提供了一种基于每个实例覆盖这些转换器的机制。
自定义EasyRestService
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服务,ComplianceService.json
[
{ id: 1, name: "Phil" },
{ id: 2, name: "John" }
]
清单5.不合规的REST服务noncompatibleService.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文档中的注释。
有关JsonRestStore期望的完整数据结构的信息,请参阅“ 相关主题”部分。
翻译自: https://www.ibm.com/developerworks/java/library/wa-jsonreststore/index.html