Chinaunix首页 | 论坛 | 博客
  • 博客访问: 385763
  • 博文数量: 69
  • 博客积分: 1984
  • 博客等级: 上尉
  • 技术积分: 953
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-28 00:43
个人简介

学无所长,一事无成

文章分类

全部博文(69)

文章存档

2015年(19)

2014年(14)

2013年(9)

2012年(17)

2010年(10)

我的朋友

分类: JavaScript

2014-07-09 17:55:01

原文:http://dojotoolkit.org/documentation/tutorials/1.10/ajax/
难度等级:初级 Dojo 版本:1.10

起步

dojo/request 允许你无需刷新页面就可以向 server 发送或接收数据(也就是大家熟知的 AJAX)。本章介绍的新特性可以使编写的代码更紧凑,执行的更快速。本章内容涉及 dojo/promise 和 dojo/Deferred,这些模块被 dojo/request 用来进行异步编程。本章不能涵盖所有内容,但请记住 promises 和 Deferreds 可以让编写非阻塞异步代码更为简单。本章之后,你再学习更多教程。

dojo/request 介绍

先看个简单例子:

  1. require(["dojo/request"], function(request){
  2.     request("helloworld.txt").then(
  3.         function(text){
  4.             console.log("The file's content is: " + text);
  5.         },
  6.         function(error){
  7.             console.log("An error occurred: " + error);
  8.         }
  9.     );
  10. });


在浏览器中,以上代码会使用 XMLHttpRequest  发出一个 HTTP GET 请求,获取 helloworld.txt ,并且返回一个   。如果请求成功,then() 当中的第一个 function 会执行,文件的文本内容是其唯一参数;如果请求失败,then() 当中的第二个 function 会执行,参数是一个 error 对象。如果我们需要发送表单数据到 server 该怎么做?响应的数据为 JSON 或 XML 又该如何处理?都没问题 -- dojo/request API 允许定制 request。

The dojo/request API

每次 request 都需要一样东西:对端 (end-point)。因此,dojo/request 的第一个参数就是请求的URL 。

web 开发者希望他们的工具足够灵活,可以按应用要求或不同的环境进行调整。dojo/request API 可以很好的达到这个要求:dojo/request 的第一个必选的参数是请求的 URL。第二个参数使用一个  object 来定制请求。其中最有用的选项是:

  • method - 大写的字符串,指明发出 request 时 HTTP 的方法类型。有几个辅助函数可以简化操作(request.get,request.post, request.put, request.del).
  • sync -  boolean 值,如果为 true,则阻塞 request ,直到 server 响应或等待超时。
  • query - 字符串,或一个包含键值对的 object,用于追加查询参数到 URL 。
  • data - 字符串,或一个包含键值对的 object,或 FormData object,用于包含需要传给 server 的数据 。
  • timeout - 设定超时值(毫秒),并设定异常处理句柄。
  • handleAs - 字符串,用来指定如何转换响应返回的有效数据,转换后送入回调函数( success handler)。可用的格式有: "text" (缺省), "json", "javascript", 和 "xml"。
  • headers - 用来设置随 request 一起发送的额外的头部信息。

我们来看一个具体例子:

  1. require(["dojo/request"], function(request){
  2.     request.post("post-content.php", {
  3.         data: {
  4.             color: "blue",
  5.             answer: 42
  6.         },
  7.         headers: {
  8.             "X-Something": "A value"
  9.         }
  10.     }).then(function(text){
  11.         console.log("The server returned: ", text);
  12.     });
  13. });


这个例子发送一个 HTTP POST 到 post-content.php;将一个简单的对象 (data那部分) 串行化并作为 POST 数据随 request 一起发送,同时附带"X-Something" 头部信息。server 响应后,返回的有效数据将传入request.post 生成的 promise 中。

例子: request.get 和request.post

下面是 dojo/request 的一些常见用法。

在页面中显示一个文本文件的内容

这个例子使用 dojo/request.get 请求一个 text 文件。这种设计一般用于提供某种静态信息如服务器环境、配置信息等,因为服务器只是响应请求,将文件发送到本地(译注:言下之意,你没法修改它),很ming在服务端维护个静态文本比维护代码要简单。

译注:由于嵌入代码包含 html 块级标签 ,显示不大正常,统一在标签前面加“!”,原始代码请参考原文。

  1. require(["dojo/dom", "dojo/on", "dojo/request", "dojo/domReady!"],
  2.     function(dom, on, request){
  3.         // Results will be displayed in resultDiv
  4.         var resultDiv = dom.byId("resultDiv");

  5.         // 给 textButton 绑定 onclick 事件句柄
  6.         on(dom.byId("textButton"), "click", function(evt){
  7.             // Request the text file
  8.             request.get("../resources/text/psalm_of_life.txt").then(
  9.                 function(response){
  10.                     // 显示文本文件内容 
  11.                     resultDiv.innerHTML = "
    "+response+"
    "
    ;
  12.                 },
  13.                 function(error){
  14.                     // 显示错误信息
  15.                     resultDiv.innerHTML = "error\">"+error+"";
  16.                 }
  17.             );
  18.         });
  19.     }
  20. );
View Demo

登录演示


下面这个例子,用 POST 请求将用户名和密码发送到 server,然后显示 server 返回结果。

  1. require(["dojo/dom", "dojo/on", "dojo/request", "dojo/dom-form"],
  2.     function(dom, on, request, domForm){
  3.  
  4.         var form = dom.byId('formNode');
  5.  
  6.         // 关联表单的 onsubmit 事件句柄
  7.         on(form, "submit", function(evt){
  8.  
  9.             // prevent the page from navigating after submit
  10.             evt.stopPropagation();
  11.             evt.preventDefault();
  12.  
  13.             // Post 数据到 server
  14.             request.post("../resources/php/login-demo.php", {
  15.                 // 发送用户名和密码
  16.                 data: domForm.toObject("formNode"),
  17.                 // 设定2秒响应超时
  18.                 timeout: 2000
  19.  
  20.             }).then(function(response){
  21.                 dom.byId('svrMessage').innerHTML = response;
  22.             });
  23.         });
  24.     }
  25. );


View Demo

Headers 演示


下面这个例子,同上个例子一样使用 POST 请求,增加了一个 Auth-Token 头部的访问。(注:Auth-Token 似乎同安全有关,可以防止 cookie 被复制盗用)

为访问头部信息,我们使用原生 Promise 的 promise.response.getHeader 方法 (从 XHR 返回的 Promise 没有这个属性)。另外,使用  promise.response.then,response 就不再是简单的 data,而是一个包含 data 属性的对象。

  1. require(["dojo/dom", "dojo/on", "dojo/request", "dojo/dom-form"],
  2.     function(dom, on, request, domForm){
  3.         // 结果会显示在 resultDiv 中
  4.  
  5.         var form = dom.byId('formNode');
  6.  
  7.         // 关联表单的 onsubmit 事件句柄
  8.         on(form, "submit", function(evt){
  9.  
  10.             // prevent the page from navigating after submit
  11.             evt.stopPropagation();
  12.             evt.preventDefault();
  13.  
  14.             // Post 数据到 server
  15.             var promise = request.post("../resources/php/login-demo.php", {
  16.                 // 发送用户名和密码
  17.                 data: domForm.toObject("formNode"),
  18.                 // 设定2秒响应超时
  19.                 timeout: 2000
  20.             });
  21.  
  22.             // 不再使用 promise.then ,而是使用 promise.response.then
  23.             promise.response.then(function(response){
  24.  
  25.                 // 从 data 属性获取信息
  26.                 var message = response.data;
  27.  
  28.                 // 访问 'Auth-Token' 头部信息
  29.                 var token = response.getHeader('Auth-Token');
  30.  
  31.                 dom.byId('svrMessage').innerHTML = message;
  32.                 dom.byId('svrToken').innerHTML = token;
  33.             });
  34.         });
  35.     }
  36. );


View Demo

JSON (JavaScript Object Notation)


在 AJAX 请求中 是一种非常通用的数据编码方式,因为它易于阅读,便于操作,格式紧凑。JSON 可以编码(encode)任何类型的数据:很多语言都包含或支持 JSON 格式,如  , , , , , 和 。

JSON encoded object

  1. {
  2.     "title":"JSON Sample Data",
  3.     "items":[{
  4.         "name":"text",
  5.         "value":"text data"
  6.     },{
  7.         "name":"integer",
  8.         "value":100
  9.     },{
  10.         "name":"float",
  11.         "value":5.65
  12.     },{
  13.         "name":"boolean",
  14.         "value":false
  15.     }]
  16. }



如果把 handleAs 设置为 "json", dojo/request 会将 response 的有效数据作为 JSON 数据进行解码,转换成 JavaScript 对象。

译注:由于嵌入代码包含 html 块级标签 ,显示不大正常,统一在标签前面加“!”,原始代码请参考原文。

  1. require(["dojo/dom", "dojo/request", "dojo/json",
  2.         "dojo/_base/array", "dojo/domReady!"],
  3.     function(dom, request, JSON, arrayUtil){
  4.         // Results will be displayed in resultDiv
  5.         var resultDiv = dom.byId("resultDiv");
  6.  
  7.         // Request the JSON data from the server
  8.         request.get("../resources/data/sample.json.php", {
  9.             // Parse data from JSON to a JavaScript object
  10.             handleAs: "json"
  11.         }).then(function(data){
  12.             // Display the data sent from the server
  13.             var html = "

    JSON Data

    "
    +
  14.                 "JSON encoded data:" +
  15.                 "" + JSON.stringify(data) + ""+
  16.                 "

    Accessing the JSON data" +

  17.                 "title " + data.title + "" +
  18.                 "items An array of items." +
  19.                 "Each item has a name and a value. The type of " +
  20.                 "the value is shown in parentheses.
    ";
  21.  
  22.             arrayUtil.forEach(data.items, function(item,i){
  23.                 html += "
    " + item.name +
  24.                     "
    " + item.value +
  25.                     " (" + (typeof item.value) + ")";
  26.             });
  27.             html += "";
  28.  
  29.             resultDiv.innerHTML = html;
  30.         },
  31.         function(error){
  32.             // Display the error returned
  33.             resultDiv.innerHTML = error;
  34.         });
  35.     }
  36. );


In addition to the encoding the data as JSON in the response, set the  header to application/json, either using server configuration such as  or adding it to the header with the server side code.

View Demo

JSONP (Javascript Object Notation with Padding)

AJAX 请求不能跨域。需要跨域的话,可以使用 。使用 JSONP时,一个 script 标签会被插入到当前页面中, the src file is requested, 服务端会将数据包裹在一个回调函数中,当 response 被解析时, the callback is called with the data as its first argument. 可以使用   发出 JSONP 请求。

看些例子吧:

使用 JSONP 从服务端请求数据,并处理响应


  1. require(["dojo/dom", "dojo/on", "dojo/request/script",
  2.         "dojo/json", "dojo/domReady!"
  3. ], function(dom, on, script, JSON){
  4.     // 结果会显示在 resultDiv 中
  5.     var resultDiv = dom.byId("resultDiv");
  6.  
  7.     // 给 makeRequest 绑定 onclick 事件句柄
  8.     on(dom.byId('makeRequest'),"click", function(evt){
  9.  
  10.         // 按钮点击后,以 JSONP 请求形式发送本地日期、时间到 server 

  11.         var d = new Date(),
  12.             dateNow = d.toString();
  13.         script.get("../resources/php/jsonp-demo.php",{
  14.             // Tell the server that the callback name to
  15.             // use is in the "callback" query parameter
  16.             jsonp: "callback",
  17.             // 发送日期、时间
  18.             query: {
  19.                 clienttime: dateNow
  20.             }
  21.         }).then(function(data){
  22.             // 显示结果
  23.             resultDiv.innerHTML = JSON.stringify(data);
  24.         });
  25.     });
  26. });

因为 response 是 JavaScript 代码,不是 JSON 数据,所以response 中的 Content-Type header 应该是 application/javascript。

View Demo

Using JSONP to request Dojo pull requests from the GitHub API


  1. require(["dojo/dom", "dojo/on", "dojo/request/script",
  2.         "dojo/dom-construct", "dojo/_base/array",
  3.         "dojo/domReady!"
  4. ], function(dom, on, script, domConstruct, arrayUtil){
  5.     var pullsNode = dom.byId("pullrequests");
  6.  
  7.     // 给 tweetButton 关联 onclick 事件句柄
  8.     on(dom.byId("pullrequestsButton"), "click", function(evt){
  9.         // 向 Dojo's GitHub repo 的开放 pull 接口发起请求
  10.         script.get("", {
  11.             // 通过 "callback" query 参数告诉 GitHub服务器,用来包裹数据的函数名称是什么


  12.             jsonp: "callback"
  13.         }).then(function(response){
  14.             // 清空 tweets 节点
  15.             domConstruct.empty(pullsNode);
  16.  
  17.             // Create a document fragment to keep from
  18.           // doing live DOM manipulation

  19.             var fragment = document.createDocumentFragment();
  20.  
  21.             // 遍历每一个 pull 请求,为每一项创建一个列表项

  22.             arrayUtil.forEach(response.data, function(pull){
  23.                 var li = domConstruct.create("li", {}, fragment);
  24.                 var link = domConstruct.create("a", {href: pull.url, innerHTML: pull.title}, li);
  25.             });
  26.  
  27.             // 将文档片段加入到列表中
  28.             domConstruct.place(fragment, pullsNode);
  29.         });
  30.     });
  31. });
View Demo
Reporting Status

 提供了一种机制,用于报告 dojo/request 提交的请求的状态 (也可是 dojo/request 中任何其他 provider)。加载 dojo/request/notify 模块就可以允许 providers 发射事件,我们就可以监听事件,据此汇报请求状态。要监听一个事件,需要给加载 dojo/request/notify 模块后的句柄传递两个参数:事件名称和监听函数( listener function)。下面是 dojo/request providers 可发射的事件:

支持的 dojo/request/notify 事件

  • start - 当请求第一次发送出去时发射
  • send - Emitted prior to a provider sending a request
  • load - 当 provider 收到响应成功的 response 时发射
  • error - 当 provider 收到错误时发射
  • done - 当 provider 完成 request 时发射,无论请求成功与否
  • stop - 当所有的在途 requests 都结束时发射

"start" 和 "stop" 上的 listener 无需参数。"send" 上的 listener 接收两个参数: 一个代表 request  的对象和一个注销函数。调用注销函数就会在 request开始前将其取消。"load", "error", 和 "done" 的 listener 接收一个参数:一个代表服务端 response 的对象。我们来看一个实例:

使用 dojo/request/notify 监控 requests 进度


译注:由于嵌入代码包含 html 块级标签 ,显示不大正常,统一在标签前面加“!”,原始代码请参考原文。
  1. require(["dojo/dom", "dojo/request", "dojo/request/notify",
  2.         "dojo/on", "dojo/dom-construct", "dojo/query",
  3.         "dojo/domReady!"],
  4.     function(dom, request, notify, on, domConstruct){
  5.         // Listen for events from request providers
  6.         notify("start", function(){
  7.             domConstruct.place("Start","divStatus");
  8.         });
  9.         notify("send", function(data, cancel){
  10.             domConstruct.place("Sent request","divStatus");
  11.         });
  12.         notify("load", function(data){
  13.             domConstruct.place("Load (response received)","divStatus");
  14.         });
  15.         notify("error", function(error){
  16.             domConstruct.place("Error","divStatus");
  17.         });
  18.         notify("done", function(data){
  19.             domConstruct.place("Done (response processed)","divStatus");
  20.             if(data instanceof Error){
  21.                 domConstruct.place("Error","divStatus");
  22.             }else{
  23.                 domConstruct.place("Success","divStatus");
  24.             }
  25.         });
  26.         notify("stop", function(){
  27.             domConstruct.place("Stop","divStatus");
  28.             domConstruct.place("Ready", "divStatus");
  29.         });
  30.  
  31.         // Use event delegation to only listen for clicks that
  32.         // come from nodes with a class of "action"
  33.         on(dom.byId("buttonContainer"), ".action:click", function(evt){
  34.             domConstruct.empty("divStatus");
  35.             request.get("../resources/php/notify-demo.php", {
  36.                 query: {
  37.                     success: this.id === "successBtn"
  38.                 },
  39.                 handleAs: "json"
  40.             });
  41.         });
  42.     }
  43. );

dojo/request/registry

 provides a mechanism to route requests based on the URL requested. Common uses of the registry are to assign a provider based on whether the request will be made to the current domain using JSON, or to a different domain using JSONP. You may also use this approach if the URLs can vary based on the operations in progress.

dojo/request/registry 语法


  1. request.register(url, provider, first);

dojo/request/registry 参数

  • url - url 是一个字符串,正则表达式(regEx),或者函数。
    • string -  如果 url 是字符串,则在 url 完全匹配后 provider 将启用。
    • regExp - 如果 url 是正则表达式,则在请求的URL 匹配后 provider 将启用。
    • function - 如果 url 是个函数,URL 以及 request 附带的选项 object 会传给函数。当函数执行返回 true 时,provider 将启用。
  • provider -  指定用来处理 request 请求的 provider 
  • first - 一个可选参数,为 boolean 值。如果为真,在已注册的 providers 前注册这个 provider(译注:有优先级吗?)。 

看一个最终的例子:

使用 dojo/request/registry ,基于请求的 URL ,关联 provider 


译注:由于嵌入代码包含 html 块级标签 ,显示不大正常,统一在标签前面加“!”,原始代码请参考原文。

  1. require(["dojo/request/registry", "dojo/request/script", "dojo/dom",
  2.         "dojo/dom-construct", "dojo/on", "dojo/domReady!"],
  3.     function(request, script, dom, domConstuct, on){
  4.         // 将所有访问 "http://" 的请求注册到 script provider
  5.         // 而对本地资源的访问将使用 xhr

  6.         request.register(/^https?:\/\//i, script);
  7.  
  8.         // When the search button is clicked
  9.         on(dom.byId("searchButton"), "click", function(){
  10.             // First send a request to twitter for all tweets
  11.             // tagged with the search string
  12.             request("", {
  13.                 query: {
  14.                     q:"#" + dom.byId("searchText").value,
  15.                     result_type:"mixed",
  16.                     lang:"en"
  17.                 },
  18.                 jsonp: "callback"
  19.             }).then(function(data){
  20.                 // 如果 tweets 节点已存在了,销毁它
  21.                 if (dom.byId("tweets")){
  22.                     domConstuct.destroy("tweets");
  23.                 }
  24.                 // 如果至少有一个返回结果
  25.                 if (data.results.length > 0) {
  26.                     // 创建一个新的 tweet 列表
  27.                     domConstuct.create("ul", {id: "tweets"},"twitterDiv");
  28.                     // 将每一个 tweet 添加成一个 li
  29.                     while (data.results.length>0){
  30.                         domConstuct.create("li", {innerHTML: data.results.shift().text},"tweets");
  31.                     }
  32.                 }else{
  33.                     // 如果没有结果返回
  34.                     domConstuct.create("p", {id:"tweets",innerHTML:"None"},"twitterDiv");
  35.                 }
  36.             });
  37.             // 接下来发送一个本地检索的 request
  38.             request("../resources/php/search.php", {
  39.                 query: {
  40.                     q: dom.byId("searchText").value
  41.                 },
  42.                 handleAs: "json"
  43.             }).then(
  44.                 function(data){
  45.                     dom.byId('localResourceDiv').innerHTML =
  46.                         "" + data.name + "" +
  47.                         " + data.url + "\">" + data.url + "";
  48.                 },
  49.                 function(error){
  50.                     // 如果没有搜索结果,本地搜索会返回一个 404
  51.                     dom.byId('localResourceDiv').innerHTML = "None";
  52.                 }
  53.             );
  54.         });
  55.     }
  56. );

最佳实践

使用 dojo/request 的最佳实践:

  • 仔细选择 request 方式method。GET 通常用于简单的非安全性数据获取。Get 一般比 POST 快。POST 通常用于发送难以通过 URL 传递的表单数据 。
  • 对需要保护的数据或者 HTTPS 页面使用 HTTPS 。
  • AJAX 请求不需刷新页面,因此大多数人都会做一个状态提示,显示 加载... 直到完成。
  • 为更好地检测及恢复 request 错误,要使用错误回调。
  • 使用有效的开发工具可以更快的解决问题。
  • 尽可能多用几种浏览器仔细测试你的代码。

小结

dojo/request 提供了一个跨浏览器兼容的 AJAX 接口,可以实现本地以及跨域请求,有设计良好的错误处理,支持信息通告,支持基于 URL 的 request 路由分发。dojo/request 调用的返回值是个 promise,运行批量发出 requests 然后异步处理 responses。页面内容可以包括多个数据源,每个数据源请求完成后数据立即可用。快使用 dojo/request 加速你的页面吧!

其他资源

阅读(1365) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~