`
java378656992
  • 浏览: 40883 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

用Dojo实现Ajax请求:XHR、跨域、及其他

 
阅读更多

在任何浏览器上方便地实现Ajax请求是每一个Ajax框架的初衷。Dojo在这方面无疑提供了非常丰富的支持。除了XMLHttpRequest之外,动态script、iframe、RPC也应有尽有,并且接口统一,使用方便,大多数情况下都只需要一句话就能达到目的,从而免除重复造轮子的麻烦。

AD: <script src="http://www.51cto.com/js/article/keywords_ad_new.js"></script>

 

在任何浏览器上方便地实现Ajax请求是每一个Ajax框架的初衷。Dojo在这方面无疑提供了非常丰富的支持。除了XMLHttpRequest之外,动态script、iframe、RPC也应有尽有,并且接口统一,使用方便,大多数情况下都只需要一句话就能达到目的,从而免除重复造轮子的麻烦。而且,Dojo一贯追求的概念完整性也在这里有所体现,换句话说,在使用Dojo的Ajax工具的过程中不会感到任何的不自然,相反更容易有触类旁通的感觉,因为API的模式是统一的,而且这里涉及到的某些概念(如Deferred对象)也贯穿在整个Dojo之中。

Dojo的XHR函数

Dojo的XMLHttpRequest函数就叫dojo.xhr,除了把自己取名美元符号之外,这好像是最直接的办法了。它定义在Dojo基本库里,所以不需要额外的require就能使用。它可以实现任何同域内的http请求。不过更常用的是dojo.xhrGet和dojo.xhrPost,它们只不过是对dojo.xhr函数的简单封装;当然根据REST风格,还有dojo.xhrPut和dojo.xhrDelete。

这些函数的参数都很统一。除了dojo.xhr的第一个参数是http方法名之外,所有的dojo.xhr*系列函数都接受同一种散列式的参数,其中包含请求的细节,例如url、是否同步、要传给服务器的内容(可以是普通对象、表单、或者纯文本)、超时设定、返回结果的类型(非常丰富且可扩展)、以及请求成功和失败时的回调。所有dojo.xhr*函数(实际上是所有IO函数)返回值也都一样,都是一个Deferred对象,顾名思义,它能让一些事情“延迟”发生,从而让API用起来更灵活。

下面的两个例子可能会带来一点直观感受:

  1. dojo.xhrGet({  
  2.     url: "something.html",  
  3.     load: function(response, ioArgs){  
  4.         //用response干一些事  
  5.         console.log("xhr get success:", response);  
  6.     return response; //必须返回response  
  7.     },  
  8.     error: function(response, ioArgs){  
  9.         console.log("xhr get failed:", response);  
  10.         return response; //必须返回response  
  11.     }  
  12. });  
  13. //Deferred对象允许用同步调用的写法写异步调用  
  14. var deferredResult = dojo.xhrPost({  
  15.     url: "something.html",  
  16.     form: formNode, //Dojo会自动将form转成object  
  17.     timeout: 3000, //Dojo会保证超时设定的有效性  
  18.     handleAs: "json" //得到的response将被认为是JSON,并自动转为object  
  19. });  
  20. //当响应结果可用时再调用回调函数  
  21. deferredResult.then(function(response){  
  22.     console.log("xhr get success:", response);  
  23.     return response; //必须返回response  
  24. }); 

首先解释一下timeout。除了IE8之外,目前大多数XMLHttpRequest对象都没有内置的timeout功能,因此必须用 setTimeout。当同时存在大量请求时,需要为每一个请求设置单独的定时器,这在某些浏览器(主要是IE)会造成严重的性能问题。dojo的做法是只用一个单独的setInterval,定时轮询(间隔50ms)所有还未结束的请求的状态,这样就高效地解决了一切远程请求(包括JSONP和 iframe)的超时问题。

值得一提的还有handleAs参数,通过设置这个参数,可以自动识别服务器的响应内容格式并转换成对象或文本等方便使用的形式。根据文档,它接受如下值:text (默认), json, json-comment-optional, json-comment-filtered, javascript, xml。

而且它还是可扩展的。其实handleAs只是告诉xhr函数去调用哪个格式转换插件,即dojo.contentHandlers对象里的一个方法。例如 dojo.contentHandlers.json就是处理JSON格式的插件。你可以方便地定制自己所需要的格式转换插件,当然,你也可修改现有插件的行为:

  1. dojo.contentHandlers.json = (function(old){  
  2.     return function(xhr){  
  3.         var json = old(xhr);  
  4.         if(json.someSignalFormServer){  
  5.             doSomthing(json);  
  6.             delete json.someSignalFormServer;  
  7.         }  
  8.         return json;  
  9.     }  
  10. })(dojo.contentHandlers.json);//一个小技巧,利用传参得到原方法 

虚拟的参数类

这里特别提一下Dojo在API设计上的两个特点。其一是虚拟的参数“类”概念:通过利用javascript对象可以灵活扩展的特点,强行规定一个散列参数属于某个“类”。例如dojo.xhr*系列函数所接受的参数就称为dojo.__XhrArgs。这个“类”并不存在于实际代码中(不要试图用 instanceof验证它),只停留在概念上,比抽象类还抽象,因此给它加上双下划线前缀(Dojo习惯为抽象类加单下划线前缀)。这样做看起来没什么意思,但实际上简化了API,因为它使API之间产生了联系,更容易记忆也就更易于使用。这一点在对这种类做“继承”时更明显。例如 dojo.__XhrArgs继承自dojo.__IoArgs,这是所有IO函数所必须支持的参数集合,同样继承自dojo.__IoArgs的还有 dojo.io.script.__ioArgs和dojo.io.iframe.__ioArgs,分别用于动态脚本请求和iframe请求。子类只向父类添加少量的属性,这样繁多的参数就具有了树形类结构。原本散列式参数是用精确的参数名代替了固定的参数顺序,在增加灵活性和可扩展性的同时,实际上增加了记忆量(毕竟参数名不能拼错),使得API都不像看起来那么好用,有了参数类的设计就缓解了这个问题。

这种参数类的做法在Dojo里随处可见,读源码的话就会发现它们都是被正儿八经地以正常代码形式声明在一种特殊注释格式里的,像这样:

  1. /*=====  
  2. dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {  
  3.     constructor: function(){  
  4.     //summary:  
  5.     //...  
  6.     //handleAs:  
  7.     //...  
  8.     //......  
  9.     }  
  10.  });  
  11.  =====*/ 

这种格式可以被jsDoc工具自动提取成文档,在文档里这些虚拟出来的类就像真的类一样五脏俱全了。

Deferred对象

另一个API设计特点就是Deferred对象的广泛使用。Dojo里的Deferred是基于MochiKit实现稍加改进而成的,而后者则是受到 python的事件驱动网络工具包Twisted里同名概念的启发。概括来说的话,这个对象的作用就是将异步IO中回调函数的声明位置与调用位置分离,这样在一个异步IO最终完成的地方,开发人员可以简单地说“货已经到了,想用的可以来拿了”,而不用具体地指出到底该调用哪些回调函数。这样做的好处是让异步IO的写法和同步IO一样(对数据的处理总是在取数据函数的外面,而不是里面),从而简化异步编程。

具体做法是,异步函数总是同步地返回一个代理对象(这就是Deferred对象),可以将它看做你想要的数据的代表,它提供一些方法以添加回调函数,当数据可用时,这些回调函数(可以由很多个)便会按照添加顺序依次执行。如果在取数据过程中出现错误,就会调用所提供的错误处理函数(也可以有很多个);如果想要取消这个异步请求,也可通过Deferred对象的cancel方法完成。

dojo.Deferred的核心方法如下:

  1. then(callback, errback); //添加回调函数  
  2. callback(result); //表示异步调用成功完成,触发回调函数  
  3. errback(error); //表示异步调用中产生错误,触发错误处理函数  
  4. cancel(); //取消异步调用 

Dojo还提供了一个when方法,使同步的值和异步的Deferred对象在使用时写法一样。例如:

  1. //某个工具函数的实现  
  2. var obj = {  
  3.     getItem: function(){  
  4.         if(this.item){  
  5.             return this.item; //这里同步地返回数据  
  6.         }else{  
  7.             return dojo.xhrGet({  //这里返回的是Deferred对象  
  8.                 url: "toGetItem.html",  
  9.                 load: dojo.hitch(thisfunction(response){  
  10.                     this.item = response;  
  11.                     return response;  
  12.                 })  
  13.             });  
  14.         }  
  15.     }  
  16. };  
  17. //用户代码  
  18. dojo.when(obj.getItem(), function(item){  
  19.     //无论同步异步,使用工具函数getItem的方式都一样  
  20. }); 

在函数闭包的帮助下,Deferred对象的创建和使用变得更为简单,你可以轻易写出一个创建Deferred对象的函数,以同步的写法做异步的事。例如写一个使用store获取数据的函数:

  1. var store = new dojo.data.QueryReadStore({...});  
  2. function getData(start, count){  
  3.     var d = new dojo.Deferred(); //初始化一个Deferred对象  
  4.     store.fetch({  
  5.         start: start,  
  6.         count: count,  
  7.         onComplete: function(items){  
  8.             //直接取用上层闭包里的Deferred对象  
  9.             d.callback(items);   
  10.         }  
  11.     });  
  12.     return d; //把它当做结果返回  

用dojo.io.script跨域

dojo.xhr* 只是XmlHttpRequest对象的封装,由于同源策略限制,它不能发跨域请求,要跨域还是需要动态创建<script>标签。Dojo 没有像JQuery一样把所有东西都封装在一起(JQuery的ajax()方法可以跨域,当然用的是JSONP,所以它不敢把自己称为xhr),而是坚持一个API只干一件事情。毕竟在大部分应用中,同域请求比跨域请求多得多,如果一个应用不需要跨域,就没必要加载相关代码。因此与xhr不同,dojo 的跨域请求组件不在基本库,而在核心库,需要require一下才能使用: 

  1. dojo.require("dojo.io.script");  

这个包里面基本上只需要用到一个函数:dojo.io.script.get()。它也返回Deferred对象,并接受类型为 dojo.io.script.__ioArgs的散列参数。受益于虚拟参数类,我们不用从头开始学习这个参数,它继承了dojo.__IoArgs,因此和dojo.xhr*系列的参数大同小异。唯一需要注意的是handleAs在这里无效了,代之以jsonp或者checkString。

前者用于实现JSONP协议,其值由服务器端指定,当script标签加载后就按照JSONP协议执行这个函数,然后Dojo会自动介入,负责把真正的数据传给load函数。需要指出的是在Dojo1.4以前,这个参数叫callbackParamName,冗长但意义明确。毕竟Dojo太早了,它成型的时候(2005)JSONP这个词才刚出现不久。现在callbackParamName还是可用的(为了向后兼容),不过处于deprecated状态。

下面的例子从flickr获取feed数据: 

  1. dojo.io.script.get({  
  2.     url: "http://www.flickr.com/services/feeds/photos_public.gne",  
  3.     jsonp: "jsoncallback"//由flickr指定  
  4.     content: {format: "json"},  
  5.     load: function(response){  
  6.         console.log(response);  
  7.         return response;  
  8.     },  
  9.     error: function(response){  
  10.         console.log(response);  
  11.         return response;  
  12.     }  
  13. }); 

与jsonp不同,checkString参数专门用于跨域获取javascript代码,它其实是那段跨域脚本里的一个有定义的变量的名字,Dojo会用它来判断跨域代码是否加载完毕,配合前面提到的timeout机制就能实现有效的超时处理。 

  1. dojo.io.script.get({  
  2.     url: "http://......", //某个提供脚本的URL  
  3.     checkString: "obj",  
  4.     load: function(response){  
  5.         //脚本加载完毕,可以直接使用其中的对象了,如obj。  
  6.         Return response;  
  7.     }  
  8. }); 

用dojo.io.iframe传数据

dojo.io 包里还有一个工具就是iframe,常用于以不刷新页面的方式上传或下载文件。这个很经典的Ajax技巧在Dojo里就是一句 dojo.io.iframe.send({...})。这个函数接受dojo.io.iframe.__ioArgs,相比 dojo.__IoArgs,它只多了一个method参数,用于指定是用GET还是POST(默认)方法发送请求。下面的例子就实现了通过无刷新提交表单来上传文件: 

  1. dojo.io.iframe.send({  
  2.     form: "formNodeId"//某个form元素包含本地文件路径  
  3.     handleAs: "html"//服务器将返回html页面  
  4.     load: onSubmitted, //提交成功  
  5.     error: onSubmitError //提交失败  
  6. }); 

目前send函数的handleAs参数支持html, xml, text, json, 和javascript五种响应格式。除了html和xml之外,使用其他格式有一个比较特别的要求,就是服务端返回的响应必须具有以下格式: 

  1. <html>  
  2.     <head></head>  
  3.     <body>  
  4.         <textarea>真正的响应内容</textarea>  
  5.     </body>  
  6. </html>  

这是因为服务器返回的东西是加载在iframe里的,而只有html页面才能在任何浏览器里保证成功加载(有个DOM在,以后取数据也方便)。加一个<textarea>则可以尽量忠实于原始文本数据的格式,而不会受到html的影响。

试试RPC(远程过程调用)

如果dojo.xhr*函数以及Deferred机制仍然无法避免代码的混乱,那RPC可能就是唯一的选择了。dojo.rpc包提供了基于“简单方法描述”语言(SMD)的RPC实现。SMD的原理类似于WSDL(Web服务描述语言),不过是基于JSON的,它定义了远程方法的名称、参数等属性,让 Dojo能创建出代理方法以供调用。

Dojo提供了两种方式实现rpc:XHR和JSONP,分别对应dojo.rpc.JsonService类和dojo.rpc.JsonpService类,用户可以根据是否需要跨域各取所需。

一个简单的例子: 

  1. var smdObj = {  
  2.     serviceType: "JSON-RPC",  
  3.     serviceURL: "http://...." 
  4.     methods: [  
  5.         {name: "myFunc", parameters: []}  
  6.     ]  
  7. };  
  8. var rpc = new dojo.rpc.JsonpService(smdObj); //传入SMD  
  9. var result = rpc.myFunc(); //直接调用远程方法,返回Deferred对象  
  10. dojo.when(result, function(result){  
  11.     //得到结果  
  12. }); 

SMD还没有一个被广泛认可的官方标准,一直以来都是Dojo社区领导着它的发展,以后这个模块也有可能发生改变,但整个RPC基本的框架会保持稳定。

结语

Ajax请求这个主题太大,本文只能挂一漏万地介绍一点dojo在这方面设计和实现的皮毛,包括基本XHR请求、动态script、iframe请求、以及RPC,并特别强调了几个有Dojo特色的设计,如timeout机制、虚拟参数类、Deferred对象等。

Dojo由Ajax领域的先驱们写就,相信从它的代码中我们一定能学到更多的东西。

原文链接:http://www.infoq.com/cn/articles/dojo-ajax-xhr

分享到:
评论

相关推荐

    dojo Ajax

    7. **dojo.io.script**: 对于需要使用JSONP(JSON with Padding)或跨域请求的情况,dojo提供了`dojo.io.script`模块。它可以帮助开发者绕过同源策略限制,与不同源的服务器进行数据交互。 8. **dojo.on**: 用于...

    dojo-0.3.1-ajax

    Dojo的AJAX功能支持跨域请求,通过设置`preventCache`、`withCredentials`等选项,可以控制跨域请求的行为。 8. **dojo.io.bind** 在Dojo 0.3.1中,`dojo.io.bind`是一个更底层的AJAX接口,它可以处理更多的细节...

    dojo-0.4.0-ajax

    1. `dojo.xhr`: 这是Dojo的核心模块,用于发起AJAX请求。它提供了一系列方法,如`xhrGet`、`xhrPost`、`xhrPut`和`xhrDelete`,分别对应HTTP的GET、POST、PUT和DELETE操作,以实现CRUD(创建、读取、更新、删除)...

    dojo相关的js

    在使用Dojo进行AJAX编程时,开发者通常会引入这个文件,然后利用Dojo的`dojo.xhr`模块来进行异步请求。例如,`dojo.xhrGet`用于发起GET请求,而`dojo.xhrPost`则对应POST请求。这些函数允许开发者传递URL、处理函数...

    dojo-0.4.1-ajax

    Dojo Toolkit通过提供异步通信功能,如xhr(XMLHttpRequest)对象的封装,使得开发者可以方便地实现AJAX请求。 2. **dojo.xhr**: Dojo的xhr模块提供了多种方法来发起AJAX请求,如`dojo.xhrGet`、`dojo.xhrPost`、`...

    ajax最好的框架之一:dojo包

    Dojo的xhr模块是其Ajax功能的核心,提供了xhrGet和xhrPost等方法,用于发送GET和POST请求。这些方法封装了浏览器兼容性问题,确保在不同环境下都能正常工作。 3. **Dojo的数据层**: Dojo的数据层包括dojo.data,...

    ajax_transportation_methods 官方文档

    除了XMLHttpRequest,还有其他几种传输方式可以实现Ajax通信: - **JSONP(JSON with Padding)**:用于跨域请求,通过动态插入`&lt;script&gt;`标签来实现。服务器返回一个JavaScript函数调用,带有要传递的数据作为参数。...

    dojo-0.4.2-ajax.rar

    Dojo 是一个强大的JavaScript工具库,它在Web开发领域中被广泛使用,特别是在构建富互联网应用程序(RIA)时。...通过深入学习和使用这个压缩包中的资源,开发者可以掌握Dojo的Ajax实现方式,并将其应用于实际项目中。

    dojo开发文档

    8. **dojo/ xhr和dojo/io**:Dojo的`dojo/xhr`和`dojo/io`模块处理AJAX通信,支持GET、POST等各种HTTP请求,还有跨域、文件上传等功能。 9. **dojo/json**:`dojo/json`模块用于JSON数据的序列化和反序列化,对于...

    dojo最新版本

    Dojo 提供了强大的异步请求功能,通过`dojo.xhr`或`dojo.request`模块,可以方便地处理HTTP请求和JSONP跨域请求,与服务器进行数据交换。 5. **dojo/_base:** `_base`是Dojo的基础模块,包含了一些核心功能,如...

    dojo-release-1.3.0b3

    5. **Dojo XHR Enhancements**:Dojo的Ajax功能在1.3.0b3版本中可能加入了对JSONP(JSON with Padding)的支持,使得跨域数据交互变得更加容易。 6. **Dojo Query Language (DQL)**:DQL是Dojo提供的一种查询语言,...

    Mastering Dojo-JavaScript and Ajax Tools for Great Web Experiences

    对于Ajax请求,Dojo简化了许多常见的操作模式,比如使用XMLHttpRequest (XHR)。这部分解释了如何使用Dojo来发起XHR请求,并处理响应。 3.3 **愿望清单与数据网格** 这里提供了一个实际的例子——构建一个愿望清单...

    Dojo 1.01 Book

    和dojo/io*模块,如dojo/io/script用于跨域请求。这部分将详细介绍如何在Dojo中处理网络数据。 6. **动画和效果**:Dojo的dojo/fx和dojox/fx模块提供了丰富的动画效果,如淡入淡出、滑动和旋转等。书中会介绍如何...

    Ajax开发实践:JSON在XMLHttpRequest中的应用

    DOJO库对XMLHttpRequest进行了封装,提供了更高级的功能,如自动处理JSONP,更好地支持跨域请求,以及提供了一套完整的Ajax解决方案,使得开发者能更便捷地实现Ajax应用。 总结起来,Ajax开发涉及到HTML、...

    DOJO 学习文档+Demo

    DOJO 提供了 `dojo/xhr*` 模块进行 AJAX 请求,如 `dojo/xhrGet` 和 `dojo/xhrPost`。这些模块处理了跨域问题,并支持各种数据格式的发送和接收。此外,`dojo/jsonp` 模块则用于 JSONP 跨域请求,适用于那些不支持 ...

    Dojo1.5官方文档

    Dojo 1.5的`dojo/xhr`模块对异步请求进行了改进,支持更多的HTTP方法(如PUT、DELETE)和跨域请求,同时提供了一致的回调机制。 7. **动画与效果(dojo/fx)** `dojo/fx`模块提供了丰富的动画效果,包括基本的...

    精通dojo 代码 javascript库

    同时,Dojo还支持JSONP和CORS,方便跨域请求。 JavaScript是Dojo的基础,理解JavaScript的高级特性对于掌握Dojo至关重要。书中会涵盖闭包、原型链、作用域、异步编程等JavaScript核心概念,确保读者能够熟练地运用...

    dojo-release-1.1.1.tar.gz

    7. **网络通信**:Dojo 提供了XMLHttpRequest(XHR)的封装,支持Ajax请求以及JSONP等跨域通信方式,使得后台数据的获取和更新更为便捷。 8. **Dijit组件库**:Dijit是Dojo的UI组件库,包含了大量的可复用、可定制...

    dojo开发帮助文档

    Dojo 是一个强大的JavaScript 库,它为Web 开发提供了丰富的功能和工具,包括模块化、用户界面构建、数据管理、动画效果以及AJAX交互等。这个“dojo开发帮助文档”是针对Dojo 1.9版本的详细指南,旨在帮助开发者深入...

    dojo-intro:介绍Dojo的主要概念

    5. **Dojo XHR**:Dojo提供了xhr模块来处理Ajax请求,支持各种HTTP方法和跨域请求,同时提供了强大的错误处理和进度反馈功能。 6. **Dojo动画**:Dojo的fx模块提供了丰富的动画效果,包括淡入淡出、滑动、旋转等,...

Global site tag (gtag.js) - Google Analytics