Dojo 的Ajax, dojo/request <14>

dojo/request 是创建从客户端到服务器端请求的一个新的API(1.8引入). 这个教程将介绍dojo/request API: 如何从服务器获请求一个文本文件, 当请求发现问题时如何处理。使用更高级的通知API,向服务器提交信息, 以及使用注册功能实现相同的代码请求不同处的数据。

开始

dojo/request 允许你不需要重新加载页面的情况下,向服务器发接和接受数据(通常称为AJAX). 引入的新代点使得代码更加简洁,执行更为快速。 这个教程里提及的dojo/promise和dojo/Deferred, 是dojo/request使用到的异步编程模式。因为不可能在一个教程里什么都讲,所以不会深入了 解Promises 和Deferred. 只需记住Promise和Deferred允许非阻塞异步编程更加容易。 在看完本教程后,你可以去看看这些教程。

介绍  dojo/request

先看看一个简单的例子:
require(["dojo/request"], function(request){
    request("helloworld.txt").then(
        function(text){
            console.log("The file's content is: " + text);
        },
        function(error){
            console.log("An error occurred: " + error);
        }
    );
});

在浏览器里, 以面的代码将会使用XMLHttpRequest  执行一个HTTP GET的请求,请求的资源为 helloworld.txt 并且返回一个 dojo/promise/Promise.  如果请求成功, then里的第一个函数会被执行,返回的文本也会作为唯一的参数传递给函数。 如果请求失败, then的第二个回调函数会被执行, 并传递一个 error 对像。  但要是给服务器发送数据? 或者返回的是JSON 或者 XML?  没问题 -dojo/reuqest API 自定义请求。

dojo/request API

每一个请求都需一件事情: 一个终点。  因为这样, dojo/request的第一个参数是 URL.

web开发者需要灵活性的工具,以适他们的应用程序和不同的环境。 dojo/request  API 也考虑了这一点: 第一个, 非可选项, 请求的URL. 第二个参数自定义请求的对像。 常用的选项是:

  • method - 一个大写的字符串,代表请求的方法。 有几个辅助函数可用来指定这个参数(request.get, request.post, request,put, request.del)
  • sync - 一个布尔值, 如果为true, 会引起阻塞,直到服务器响应或者请求超时。
  • query - 一个字符串或键值对像, 附加到URL的查询参数。
  • data - 字符, 键值对像 或者 FormData 对像,要传递给服务器的数据
  • timeout -  以毫秒为单位的时间,请求超时。
  • handleAs - 一个字符串, 在将返回的数据传递给success处理函数之前,如何将已载入的数据进行转换, 可能的数据格式有 "text" (默认), "json", "javascript"  和 "xml"
  • headers- 键值对像, 包含额外的请求头信息。
让我们看一个使用了这些选项的例子:

require(["dojo/request"], function(request){
    request.post("post-content.php", {
        data: {
            color: "blue",
            answer: 42
        },
        headers: {
            "X-Something": "A value"
        }
    }).then(function(text){
        console.log("The server returned: ", text);
    });
});

以上代码执行了一个指向 post-content.php的 post请求。 一个简单的对像(data) 被序列化然后作为请求数据发送,以及"X-somethine" 请求头也会作为请求数据发送。 当服务器响应时,  会从request.post返回promise的值。

Examples: request.get 和 request.post

以下是dojo/request常见用途

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

这个例子中使用dojo/request.get 请求一个文本文件。 最常用于网站的条款声明或者隐私声明。 因为只在浏览者明确需要时,才将文本文件发送给它们。 并且文件文件比嵌入了代码的文件更易于维护。


require(["dojo/dom", "dojo/on", "dojo/request", "dojo/domReady!"],
    function(dom, on, request){
        // Results will be displayed in resultDiv
        var resultDiv = dom.byId("resultDiv");
 
        // Attach the onclick event handler to the textButton
        on(dom.byId("textButton"), "click", function(evt){
            // Request the text file
            request.get("../resources/text/psalm_of_life.txt").then(
                function(response){
                    // Display the text file content
                    resultDiv.innerHTML = "<pre>"+response+"</pre>";
                },
                function(error){
                    // Display the error returned
                    resultDiv.innerHTML = "<div class=\"error\">"+error+"<div>";
                }
            );
        });
    }
);



登录演示

在接下来的例子中,一个POST请求用于发送 username和password 到服务器, 并显示服务器返回的结果。
require(["dojo/dom", "dojo/on", "dojo/request", "dojo/dom-form"],
    function(dom, on, request, domForm){
 
        var form = dom.byId('formNode');
 
        // Attach the onsubmit event handler of the form
        on(form, "submit", function(evt){
 
            // prevent the page from navigating after submit
            evt.stopPropagation();
            evt.preventDefault();
 
            // Post the data to the server
            request.post("../resources/php/login-demo.php", {
                // Send the username and password
                data: domForm.toObject("formNode"),
                // Wait 2 seconds for a response
                timeout: 2000
 
            }).then(function(response){
                dom.byId('svrMessage').innerHTML = response;
            });
        });
    }
);


请求头演示

在接下来例子中, 使用如上的post请求,并且访问 Auth-token头部信息。

为了访问headers信息, 使用promise.reponse.getHeader 方法(原生的Promise是从XHR对像返回的,它没有这个属性, 只能是 promise.response). 另外, 当使用promise.response.then,  response uq将不是数据,而是一个包含数据属性的对像。
require(["dojo/dom", "dojo/on", "dojo/request", "dojo/dom-form"],
    function(dom, on, request, domForm){
        // Results will be displayed in resultDiv
 
        var form = dom.byId('formNode');
 
        // Attach the onsubmit event handler of the form
        on(form, "submit", function(evt){
 
            // prevent the page from navigating after submit
            evt.stopPropagation();
            evt.preventDefault();
 
            // Post the data to the server
            var promise = request.post("../resources/php/login-demo.php", {
                // Send the username and password
                data: domForm.toObject("formNode"),
                // Wait 2 seconds for a response
                timeout: 2000
            });
 
            // Use promise.response.then, NOT promise.then
            promise.response.then(function(response){
 
                // get the message from the data property
                var message = response.data;
 
                // Access the 'Auth-Token' header
                var token = response.getHeader('Auth-Token');
 
                dom.byId('svrMessage').innerHTML = message;
                dom.byId('svrToken').innerHTML = token;
            });
        });
    }
);

查看示例

JSON 

JSON 常用于对AJAX请求的数据进行编码, 因为它的易读,易用,以及简洁的特性, JSON可用于编码任意的数据类型: JSON 支持多种语言: php, java, perl, python, ruby 和 ASP.

JSON编码后的对像

{
	"title":"JSON Sample Data",
	"items":[{
		"name":"text",
		"value":"text data"
	},{
		"name":"integer",
		"value":100
	},{
		"name":"float",
		"value":5.65
	},{
		"name":"boolean",
		"value":false
	}]
}

当 handleAs 被设置为"json", dojo/request 会对返回的数据以JSON格式处理, 并将返回的数据解析为一个Javascript对像。
require(["dojo/dom", "dojo/request", "dojo/json",
        "dojo/_base/array", "dojo/domReady!"],
    function(dom, request, JSON, arrayUtil){
        // Results will be displayed in resultDiv
        var resultDiv = dom.byId("resultDiv");
 
        // Request the JSON data from the server
        request.get("../resources/data/sample.json.php", {
            // Parse data from JSON to a JavaScript object
            handleAs: "json"
        }).then(function(data){
            // Display the data sent from the server
            var html = "<h2>JSON Data</h2>" +
                "<p>JSON encoded data:</p>" +
                "<p><code>" + JSON.stringify(data) + "</code></p>"+
                "<h3>Accessing the JSON data</h3>" +
                "<p><strong>title</strong> " + data.title + "</p>" +
                "<p><strong>items</strong> An array of items." +
                "Each item has a name and a value.  The type of " +
                "the value is shown in parentheses.</p><dl>";
 
            arrayUtil.forEach(data.items, function(item,i){
                html += "<dt>" + item.name +
                    "</dt><dd>" + item.value +
                    " (" + (typeof item.value) + ")</dd>";
            });
            html += "</dl>";
 
            resultDiv.innerHTML = html;
        },
        function(error){
            // Display the error returned
            resultDiv.innerHTML = error;
        });
    }
);

!* 另外还需要设置response以JSON来编码数据, 可以设置Content-type 的头部信息为application/json, 或者使用服务器端的配置,如Apache的 addType 或者 在服务器端代码里添加  header信息。



JSONP(Javascript Object Notation with Padding)

AJAX请求局性于当前的域名下。 如果你需要请求的数据是不同的域名, 你可以使用JSONP. 当使用JSONP时, 一个script 标签会被插入到当前页面, src会设置为请求文件的URL, 服务器会使用callback函数 封装数据。 并且当响应执行完时, 会调用callback 函数,并将传递返回的数据给callback. JSONP请求是通过dojo/request/script.

让我们看些例子

使用JSONP从服务器请求处据,并处理响应
require(["dojo/dom", "dojo/on", "dojo/request/script",
        "dojo/json", "dojo/domReady!"
], function(dom, on, script, JSON){
    // Results will be displayed in resultDiv
    var resultDiv = dom.byId("resultDiv");
 
    // Attach the onclick event handler to the makeRequest button
    on(dom.byId('makeRequest'),"click", function(evt){
 
        // When the makeRequest button is clicked, send the current
        // date and time to the server in a JSONP request
        var d = new Date(),
            dateNow = d.toString();
        script.get("../resources/php/jsonp-demo.php",{
            // Tell the server that the callback name to
            // use is in the "callback" query parameter
            jsonp: "callback",
            // Send the date and time
            query: {
                clienttime: dateNow
            }
        }).then(function(data){
            // Display the result
            resultDiv.innerHTML = JSON.stringify(data);
        });
    });
});

由于响应是Javascript, 而不是Json,  响应的头部信息 Content-Type应该设置为application/javascript 


使用JSONP来请求Github API上的 dojo pull请求.
require(["dojo/dom", "dojo/on", "dojo/request/script",
        "dojo/dom-construct", "dojo/_base/array",
        "dojo/domReady!"
], function(dom, on, script, domConstruct, arrayUtil){
    var pullsNode = dom.byId("pullrequests");
 
    // Attach the onclick event handler to tweetButton
    on(dom.byId("pullrequestsButton"), "click", function(evt){
        // Request the open pull requests from Dojo's GitHub repo
        script.get("https://api.github.com/repos/dojo/dojo/pulls", {
            // Use the "callback" query parameter to tell
            // GitHub's services the name of the function
            // to wrap the data in
            jsonp: "callback"
        }).then(function(response){
            // Empty the tweets node
            domConstruct.empty(pullsNode);
 
            // Create a document fragment to keep from
            // doing live DOM manipulation
            var fragment = document.createDocumentFragment();
 
            // Loop through each pull request and create a list item
            // for it
            arrayUtil.forEach(response.data, function(pull){
                var li = domConstruct.create("li", {}, fragment);
                var link = domConstruct.create("a", {href: pull.url, innerHTML: pull.title}, li);
            });
 
            // Append the document fragment to the list
            domConstruct.place(fragment, pullsNode);
        });
    });
});
查看Demo

状态报告

dojo/request/notify 提供了一个报告请求状态的机制。 加载完dojo/request/nofity模块后,它允许请求的提供者(request,response, request.get, request)可以触发事件,这些事件可以用于监听和报告request的状态。 为了监听一个事件, 调用dojo/request/notify模块的返回值,并传递两个参数给它: 事件名称 和 监听函数。 dojo/request的请求提供者,可以触发以下事件:
dojo/request/notify 支持的事件类型
  • start - 当多个请求中的第一个请求开始时
  • send - 当一个提供者发送请求之前触发
  • load - 当提供者成功接受了一个响应时触发
  • error - 当提供者接受到一个错误时触发
  • done - 当提供者完成一个请求时触发,不管是成功或者失败
  • stop - 当多个请求完成时触发
start和stop的监听器不会接受参数。 send的监听器接受两个参数: 一个代表request 对像象 和一个cancel 函数。 调用 cancel函数将会在请求开始前取消这个请求。 "load", "error", "done"的监听器只有一个参数: 服务器的响应对像。 让我们看看实际的例子。

使用 dojo/request/notify 监控请求的进度

require(["dojo/dom", "dojo/request", "dojo/request/notify",
        "dojo/on", "dojo/dom-construct", "dojo/query",
        "dojo/domReady!"],
    function(dom, request, notify, on, domConstruct){
        // Listen for events from request providers
        notify("start", function(){
            domConstruct.place("<p>Start</p>","divStatus");
        });
        notify("send", function(data, cancel){
            domConstruct.place("<p>Sent request</p>","divStatus");
        });
        notify("load", function(data){
            domConstruct.place("<p>Load (response received)</p>","divStatus");
        });
        notify("error", function(error){
            domConstruct.place("<p class=\"error\">Error</p>","divStatus");
        });
        notify("done", function(data){
            domConstruct.place("<p>Done (response processed)</p>","divStatus");
            if(data instanceof Error){
                domConstruct.place("<p class=\"error\">Error</p>","divStatus");
            }else{
                domConstruct.place("<p class=\"success\">Success</p>","divStatus");
            }
        });
        notify("stop", function(){
            domConstruct.place("<p>Stop</p>","divStatus");
            domConstruct.place("<p class=\"ready\">Ready</p>", "divStatus");
        });
 
        // Use event delegation to only listen for clicks that
        // come from nodes with a class of "action"
        on(dom.byId("buttonContainer"), ".action:click", function(evt){
            domConstruct.empty("divStatus");
            request.get("../resources/php/notify-demo.php", {
                query: {
                    success: this.id === "successBtn"
                },
                handleAs: "json"
            });
        });
    }
);

dojo/request/registry

dojo/request/registry 为请求提供了一种基于URL的路由机制。registry 常用于为JSON 或者 JSONP分配请求提供者(XHR或者Script). 你也可以用于一个URL会经常变化的情况。

dojo/request/registry 语法
request.register(url, provider, first);

dojo/request/registry 参数
  • URL - 字符串, 正则表达式或者一个函数
String - 如果url 为字符串, 只有当url 准确匹配, 提供者才会被使用
regExp -  如果是正则表达式, 当正则表达式与请求的URL匹配时,提供者才会被使用
function - 如果url是一个函数, 将会传递URL和一个可选的request 对像给这个函数。 如果函数返回true, provider才会被使用。
  • provider - 用于处理请求的provider.
  • first - 可选的布尔参数, 如果为true,  会在其它已经注册的提供者之前注册这个提供者。
让我们看最终的例子

使用dojo/request/registry 来基于不同请求的URL来分配 提供者(provider)

require(["dojo/request/registry", "dojo/request/script", "dojo/dom",
        "dojo/dom-construct", "dojo/on", "dojo/domReady!"],
    function(request, script, dom, domConstuct, on){
        // Registers anything that starts with "http://"
        // to be sent to the script provider,
        // requests for a local search will use xhr
        request.register(/^https?:\/\//i, script);
 
        // When the search button is clicked
        on(dom.byId("searchButton"), "click", function(){
            // First send a request to twitter for all tweets
            // tagged with the search string
            request("http://search.twitter.com/search.json", {
                query: {
                    q:"#" + dom.byId("searchText").value,
                    result_type:"mixed",
                    lang:"en"
                },
                jsonp: "callback"
            }).then(function(data){
                // If the tweets node exists, destroy it
                if (dom.byId("tweets")){
                    domConstuct.destroy("tweets");
                }
                // If at least one result was returned
                if (data.results.length > 0) {
                    // Create a new tweet list
                    domConstuct.create("ul", {id: "tweets"},"twitterDiv");
                    // Add each tweet as an li
                    while (data.results.length>0){
                        domConstuct.create("li", {innerHTML: data.results.shift().text},"tweets");
                    }
                }else{
                    // No results returned
                    domConstuct.create("p", {id:"tweets",innerHTML:"None"},"twitterDiv");
                }
            });
            // Next send a request to the local search
            request("../resources/php/search.php", {
                query: {
                    q: dom.byId("searchText").value
                },
                handleAs: "json"
            }).then(
                function(data){
                    dom.byId('localResourceDiv').innerHTML =
                        "<p><strong>" + data.name + "</strong><br />" +
                        "<a href=\"" + data.url + "\">" + data.url + "</a><br />";
                },
                function(error){
                    // If no results are found, the local search returns a 404
                    dom.byId('localResourceDiv').innerHTML = "<p>None</p>";
                }
            );
        });
    }
);

看 demo

最佳实现


 dojo/request注意事项:

  • 在选择请求方法时小心, 一般, GET  用于简单的请求数据,不需要安全的考虑。 GET 通常要比  POST快。 POST 通常用于发送数据和当数据不应该由URL传递时。
  • 在https页面,应该使用 HTTPS来保护数据。
  • 由于AJAX请求不会刷新页面, 所以应该为用户提供状态更新, 从 loading .... 到 完成
  • 错误回调函数应该被用于检测和请求错误恢复
  • 使用有效的开发者工具来快速解决问题
  • 尽可能用多个浏览器测试。

总结

dojo/request  为请求(当前域名的请求或者其它域名的请求)提代了一个跨浏览器兼容 的AJAX 接口,包括优雅的错误处理, 支持通知 和 基于 URL路由请求。 dojo/request会返回一个promise(承诺), 它允许异步发送和处理一系列的请求。 网页可以包含多个源的内容, 并且使用每个请求数据。 用dojo/request 来增加你的页面吧!

资源


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值