随着Dojo向着2.0大步迈进,我们已开始致力于为开发人员提供能在任何JavaScript环境下保持高效生产力的工具。这意味着我们所创建的API必须在所有环境下都保持一致。从这个角度看,有
在Dojo1.8发布之际,我们隆重推出dojo/requestAPI。这套API在所有的浏览器、所有请求方法、甚至所有JavaScript环境上都是一致的: [javascript] view plain copy
- <span style="font-size:14px;">require(["dojo/request"], function(request){
- var promise = request(url, options);
- promise.then(
- function(data){
- },
- function(error){
- }
- );
- promise.response.then(
- function(response){
- },
- function(error){
- }
- );
- request.get(url, options).then(...);
- request.post(url, options).then(...);
- request.put(url, options).then(...);
- request.del(url, options).then(...);
- });</span>
- method:用于本请求的HTTP方法(默认是GET,dojo/request/script会忽略这个参数)
- query:形如key=value的字符串,或者形如{key: 'value'}的对象,包含所有的query参数
- data:字符串或对象(会被dojo/io-query.objectToQuery串行化成字符串),表示需要发送的数据(GET和DELET请求会忽略这个参数)
- handleAs:表示如何处理服务器端响应的字符串,默认"text",其他可能的值包括'json', 'javascript',以及'xml'
- headers:形如{'Header-Name': 'value'}的对象,包含请求所需要的各种头部属性
- timeout:表示等待多少毫秒算超时的整数,一旦超时将取消请求并"拒绝(reject)"所返回的promise。
dojo/request所返回的promise对象具有一个普通promise没有的附加属性:response。这个属性本身也是一个promise,它将提供一个对象来更详细地描述这次响应:
- url:发起请求的最终URL(加上了query字符串)
- options:请求相关的参数
- text:响应中数据的字符串表示
- data: 对响应进行处理后返回的数据(如果handles参数指定了有效的解析方式)
- getHeader(headerName):用于获取请求头部参数的函数;如果某个provider没有提供头部信息,这个函数将返回null。
Provider(提供者,这里指能够提供某种请求处理方式的模块 ——译注)
在幕后,dojo/request是通过provider来发起请求的。对于每一个平台dojo都选好了一个合适的默认provider:浏览器使用dojo/request/xhr,Node.js使用dojo/request/node。需要指出的是,比较新的浏览器(IE9+,FF3.5+,Chrome7+,Safari4+)将使用心得XMLHttpRequest2事件,而不是XMLHttpRequest的onreadystatechange,那只有在更老的浏览器中才会使用。另外,Node.js的provider直接使用了http和https模块,这意味着不用在服务器端部署任何接受XMLHttpRequest请求的中间层。如果需要使用一个非默认的provider(例如JSON-P的provider),有如下三种选择:直接使用非默认provider;把它配置成默认provider;或者配置请求的注册信息。
由于所有的provider都遵循dojo/request API,非默认的provider是可以直接使用的。dojo/request的架构设计思想类似于dojo/store。这意味着如果你只有一些JSON-P服务,你可以直接使用dojo/request/script而不用改变基本的API签名。与其他两种方式比较起来,这种使用非默认provider的方式灵活性较差,但它是的确是一种完全有效的方式。
另一种使用非默认provider的方式是将它配置成默认provider。如果我们知道我们的应用只会使用这一个provider,那这样做就非常有帮助。配置默认provider其实非常简单,就是把provider的模块ID设置成dojoConfig的requestProvider属性: [html] view plain copy
- <span style="font-size:14px;"><script>
- var dojoConfig = {
- requestProvider: "dojo/request/script"
- };
- </script>
- <script src="path/to/dojo/dojo.js"></script></span>
虽然比起直接使用非默认provider来,将其配置成默认能为我们提供更大的灵活性,但这么做仍然不能只通过一个API(dojo/request)就做到根据预设条件来自动使用不同provider的能力。假设我们的应用有一些数据服务,其中一个服务需要一组用于身份验证的头部信息,而另一个需要完全不同的另一组头部信息。或者一个需要JSON-P而另一个需要XMLHttpRequest。这种情况下就是dojo/request/registry闪亮登场的时候了。
注册机制
Dojox中有一个存在了很久但并没有得到广泛使用的模块dojox/io/xhrPlugins。这个模块可以让dojo.xhr*成为所有请求的接口,无论这些请求是通过JSONP发送还是iframe,甚至是其他用户自定义的方式。这个统一接口的思想非常有用,因此被沿用到dojo/request/registry中。dojo/request/registry同样遵循dojo/request API(因此它本身就可以作为一个provider),不过增加了一个register函数: [javascript] view plain copy
- <span style="font-size:14px;">// 如果一个请求的URL是"some/url",provider就会被用来处理这个请求
- registry.register("some/url", provider);
- // 如果一个请求的URL以"some/url"开始,provider就会被用来处理这个请求
- registry.register(/^some\/url/, provider);
- // 如果一个请求是一HTTP GET方法发送的,provider就会被用来处理这个请求
- registry.register(
- function(url, options){
- return options.method === "GET";
- },
- provider
- );
- // 如果不能匹配到任何已注册的条件,默认provider将被使用
- </span>
- <span style="font-size:14px;"><script>
- var dojoConfig = {
- requestProvider: "dojo/request/registry"
- };
- </script>
- <script src="path/to/dojo/dojo.js"></script>
- <script>
- require(["dojo/request", "dojo/request/script"],
- function(request, script){
- request.register(/^\/jsonp\//, script);
- ...
- }
- );
- </script></span>
- <span style="font-size:14px;"><script>
- var dojoConfig = {
- requestProvider: "dojo/request/registry!my/authProvider"
- };
- </script>
- <script src="path/to/dojo/dojo.js"></script>
- <script>
- require(["dojo/request", "dojo/request/script"],
- function(request, script){
- request.register(/^\/jsonp\//, script);
- ...
- }
- );
- </script></span>
注册provide功能的强大可能还不是那么明显。现在让我们来看几个让注册功能大显身手的场景。首先,考虑一个应用,它的服务器端API是在不断变化的。也就是说虽然我们知道每一个具体的终端服务,但我们不知道会需要什么样的头部信息,甚至也不知道响应中会返回什么样的JSON对象。我们可以很容易地为每一项服务注册一个临时provider,然后立马开始开发用户界面。假设我们猜想/service1将在items属性中返回JSON格式的数据项,而/service2将在data属性中返回这些数据项: [javascript] view plain copy
- <span style="font-size:14px;">request.register(/^\/service1\//, function(url, options){
- var promise = xhr(url, lang.delegate(options, { handleAs: "json" })),
- dataPromise = promise.then(function(data){
- return data.items;
- });
- return lang.delegate(dataPromise, {
- response: promise.response
- });
- });
- request.register(/^\/service2\//, function(url, options){
- var promise = xhr(url, lang.delegate(options, { handleAs: "json" })),
- dataPromise = promise.then(function(data){
- return data.data;
- });
- return lang.delegate(dataPromise, {
- response: promise.response
- });
- });</span>
这种解耦也可以拓展到测试上。通常在单元测试中无法获得远程服务:远程数据可能变化,远程服务器也可能不可用。这也是为什么推荐使用静态数据做测试。但如果我们的widget和用户界面把服务终端和对应请求都写死在里面了,我们还怎么去测试呢?而如果我们使用了dojo/request/registry,只需要注册一个专门为测试任务返回静态数据的provider就行了,所有的API调用都不需要修改,现有应用中不需要重写任何代码。
结论
可见,dojo/request是为开发者编写的:对于简单的场景有简单的API,对于复杂场景也有非常灵活的选项。相关资料
更多关于dojo/request的学习资料请参考:- Ajax with dojo/request tutorial
- dojo/request reference guide
- dojo/request API