什么是异步通信层
- Microsoft AJAX Library的组长部分之一
- 负责ASP.NET AJAX框架中所有的客户端与服务器端的通信
- 其默认实现了封装了XMLHttpRequest的功能
一个使用XMLHttpRequest发出AJAX请求的示例
创建一个名为RandomNumber.ashx的一般处理程序
<%@ WebHandler Language="C#" Class="RandomNumber" %> using System; using System.Web; public class RandomNumber : IHttpHandler { private static Random random = new Random(DateTime.Now.Millisecond); public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/plain"; context.Response.Write(random.Next()); } public bool IsReusable { get { return false; } } }
创建一个htm页面
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script language="javascript" type="text/javascript"> function getXMLHttpRequest() {//获得XMLHttpRequest对象 if (window.XMLHttpRequest) {//如果有原生的XMLHttpRequest,IE6+、firefox都有 return new window.XMLHttpRequest(); } else {//如果没有原生的XMLHttpRequest var progIDs = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP']; for (var i = 0; i < progIDs.length; i++) { try { var xmlHttp = new ActiveXObject(progIDs[i]); return xmlHttp; } catch (ex) { } } return null; } } function sendRequest() { var xhr = getXMLHttpRequest(); //第一个参数:发送请求的方式。第二个参数:请求发给的地址。第三个参数:true异步更新(默认),false阻塞更新 xhr.open("POST", "RandomNumber.ashx", true); xhr.onreadystatechange = function() {//指定回调函数 onReadyStateChange.apply(xhr);//将xhr作为this指针 } xhr.send(null);//发送请求,参数为请求的body } function onReadyStateChange() { if (this.readyState == 4) {//readyState==4表示得到结果 if (this.status == 200) {//status==200表示得到结果正常 alert(this.responseText); } } else { } } </script> </head> <body> <input type="button" value="Send" onclick="sendRequest()" /> </body> </html>
注释中我已经写的很清楚,XMLHttpRequest在当他的readyState改变以后,调用我们定义的onReadyStateChange,然后通过判断一些状态来验证是否得到了我们想要数据,而不是服务器端抛出的错误等等
Micorsoft AJAX Library异步通信层的组成
- 均在Sys.Net命名空间下
- WebRequest类:负责手机存储请求信息
- WebRequestExecutor类:负责发送请求,反馈服务器端回复的结果
- WebRequestManager类:用户管理异步通讯层与服务器端的通信
WebRequest类成员
- completed事件:得到回复后出发
- completed方法:引发completed事件
- getResolvedUrl方法:获得完整的URL
- invoke方法:发送请求
- body属性:发送到服务器的内容
- executor属性:发送请求的Executor对象
- headers属性:请求的头信息集合
- httpVerb属性:请求使用的HTTP方法
- timtout属性:超时时间
- url属性:请求的URL
- userContext属性:附加于WebRequest的对象
WebRequestExexutor成员
- abort方法:取消当前请求
- executorRequest方法:执行请求
- getAllResponseHeaders方法:获取回复内所有的头文件
- getResponseHeader方法:获得回复指定的头信息
- aborted属性:表示请求是否被取消
- responseAvailable属性:表示是否得到了正确的结果
- responseData属性:获得字符串形式的回复内容
- started属性:表示请求是否已经开始
- statusCode属性:表示回复状态的代码
- statusText属性:表示回复状态的文字
- timedOut属性:表示是否超时
- xml属性:获得xml形式的回复内容
- webRequest属性:获得当前正在执行的WebRequest对象
使用异步通信层的示例
首先创建一个名为Complex.ashx的一般处理程序
<%@ WebHandler Language="C#" Class="Complex" %> using System; using System.Web; public class Complex : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string action = context.Request.Headers["action"];//得到Header中key为action的值 if (action == "normal") { context.Response.Write("You've sent:" + context.Request.Params["data"]);//把传过来的data值做处理,然后写回 } else if (action == "error") { throw new Exception();//抛出一个异常 } else { System.Threading.Thread.Sleep(5000);//线程停止5秒,如果客户端设置超时小于五秒,则会造成一个超时错误 } } public bool IsReusable { get { return false; } } }
然后创建一个aspx页面
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script language="javascript" type="text/javascript"> var webRequest=null; function sendRequest(action) { webRequest = new Sys.Net.WebRequest(); webRequest.set_url("Complex.ashx");//设置请求路径 webRequest.get_headers()["action"] = action;//设置一个key为action的Header webRequest.set_body("data=" + encodeURIComponent("Xiaoyaojian"));//设置发送的内容 webRequest.set_httpVerb("POST");//设置请求使用的HTTP方法 webRequest.set_timeout(3000);//设置超时时间 webRequest.add_completed(onCompleted);//添加完成时候的回调函数 webRequest.invoke();//执行请求 } function onCompleted(response, eventArgs) { if (response.get_aborted()) {//判断是否被取消 alert("Request aborted!"); } else if (response.get_responseAvailable()) {//判断得到的信息是否正确 var statusCode = response.get_statusCode();//得到状态码 if (statusCode < 200 || statusCode >= 300) {//状态码小于200或者大于等于300,则表示出现了错误 alert("Error occurred!"); } else { alert(response.get_responseData());//回复的信息 } } else { if (response.get_timedOut()) {//判断是否为超时 alert("Request timed out!"); } else { alert("Error occurred!"); } } } </script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <input type="button" value="Normal" onclick="sendRequest('normal');" /> <input type="button" value="Error" onclick="sendRequest('error');" /> <input type="button" value="Time out" onclick="sendRequest('ad');" /> <input type="button" value="Abort" onclick="webRequest.get_executor().abort()" /> </form> </body> </html>
点击Normal,得到正常的结果,点击Error,出现一个错误,点击Time out,得到一个超时错误,点击Abort,可以取消一个请求,我们可以使用先点击Time out,然后在三秒内点击Abort来得到一个取消的效果
WebRequestManager成员
- invokingRequest事件:即将发送请求时候触发,可用于取消某个请求
- completedRequest事件:请求结束时候触发,他早于WebRequest对象的completed事件
- defaultTimeout属性:默认超时时间
- defaultExecutorType属性:默认的发送请求的Executor类型
使用WebRequestManager的事件的示例
创建一个aspx页面
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <%= DateTime.Now %><hr /> <asp:Button ID="Button1" runat="server" Text="Button" /> </ContentTemplate> </asp:UpdatePanel> <script language="javascript" type="text/javascript"> Sys.Net.WebRequestManager.add_invokingRequest(function(sender, eventArgs) { if (confirm("Concel the partial rendering?")) {//在即将发起请求的时候,参数提示,让用户确认 eventArgs.set_cancel(true);//取消操作 } }); Sys.Net.WebRequestManager.add_completedRequest(function() {//在请求结束以后,WebRequest的completed没有被触发之前,给出一个提示 alert("Response received!"); }); </script> </form> </body> </html>
示例简单的。。。我都不知道该说什么了,就是响应了WebRequestManager的两个事件。。。。。。
WebRequestExecutor类成员
- abort方法:取消当前操作
- executeRequest方法:执行请求
- getAllResponseHeaders方法:获取回复中所有的头信息
- getResponseHeader方法:获取回复中指定KEY的头信息
- aborted属性:表示请求是否被取消
- responseAvailable属性:表示是否得到了正确的结果
- responseData属性:获得字符串形式的回复内容
- started属性:表示请求是否已经开始
- statusCode属性:表示回复状态代码
- statusText属性:表示回复状态的问题
- timedOut属性:表示回复是否为超时
- xml属性:获得xml形式的回复内容
- webRequest属性:获得当前正在执行的WebRequest对象
实现ScriptReferenceExecutor
- 实现简单属性(aborted,responseAvailable,responseData,started,statusCode,statusText,timedOut,xml)
- 实现executeRequest方法(发送信息,监听超时)
- 实现完成、超时、取消逻辑
- 清楚超时监听和其他一些辅助对象
- 调用WebRequest的completed方法
- 实现不支持的方法:getAllResponseHeaders方法,getResponseHeader方法
一个实现executeRequest方法的示例
首先创建一个名为ScriptRerenceExcutor.js的js文件
Sys.Net.ScriptReferenceExecutor = function() {//构造函数 Sys.Net.ScriptReferenceExecutor.initializeBase(this);//调用基类的构造函数 //初始化一些私有的变量 this._responseAvailable = false; this._timedOut = false; this._aborted = false; this._started = false; this._responseData = null; this._statusCode = 0; this._statusText = null; this._uniqueKey = null; this._timer = null; this._scriptElement = null; } Sys.Net.ScriptReferenceExecutor.prototype = { //定义一些简单属性 get_responseAvailable: function() { return this._responseAvailable; }, get_timedOut: function() { return this._timedOut; }, get_aborted: function() { return this._aborted; }, get_started: function() { return this._started; }, get_responseData: function() { return this._responseData; }, get_statusCode: function() { return this._statusCode; }, get_statusText: function() { return this._statusText; }, //把get_responseData()得到的结果,然后进行XML转换 get_xml: function() { return new XMLDOM(this.get_responseData()); }, executeRequest: function() { this._started = true;//表示执行已经开始 var request = this.get_webRequest();//得到webRequest对象的信息 var serializer = Sys.Serialization.JavaScriptSerializer;//Microsoft AJAX Library提供给我们进行JSON序列化和反序列化的方法 //以下是拼接QueryString的过程 var scriptUrl = request.get_url() + "?"; scriptUrl += (("headers=") + encodeURIComponent(serializer.serialize(request.get_headers()))); scriptUrl += ("&body=" + encodeURIComponent(serializer.serialize(request.get_body()))); var uniqueKey = this._uniqueKey = this._generateUniqueKey();//此字段确定加载的SciptRequestExecutor是由谁发起的 scriptUrl += ("&uniqueKey=" + encodeURIComponent(serializer.serialize(uniqueKey))); Sys.Net.ScriptReferenceExecutor._executors[uniqueKey] = this; //把字的uniqueKey保存起来 var scriptElement = this._scriptElement = document.createElement("script"); scriptElement.type = "text/javascript"; scriptElement.language = "javascript"; scriptElement.src = scriptUrl; document.getElementsByTagName("head")[0].appendChild(scriptElement); //设置超时 var timeout = request.get_timeout(); if (timeout > 0) { this._timer = window.setTimeout( Function.createDelegate(this, this._onTimeout), timeout); } }, _onTimeout: function() {//设置超时执行的操作 this.complete(null, null, null, false, true, false); }, abort: function() {//设置取消操作后执行的操作 this.complete(null, null, null, false, false, true); }, _generateUniqueKey: function() { return Math.random().toString();//通常已经足够使用,当然还是可以采用其他一些更严格的做法 }, complete: function(statusCode, statusText, body, responseAvailable, timedOut, aborted) { this._statusCode = statusCode; this._statusText = statusText; this._responseData = body; this._responseAvailable = responseAvailable; this._timedOut = timedOut; this._aborted = aborted; if (this._timer) { window.clearTimeout(this._timer); } document.getElementsByTagName("head")[0].removeChild(this._scriptElement); delete Sys.Net.ScriptReferenceExecutor._executors[this._uniqueKey]; this.get_webRequest().completed(Sys.EventArgs.Empty); } } Sys.Net.ScriptReferenceExecutor.registerClass("Sys.Net.ScriptReferenceExecutor", Sys.Net.WebRequestExecutor); Sys.Net.ScriptReferenceExecutor._executors = new Object(); Sys.Net.ScriptReferenceExecutor.complete = function(uniqueKey, statusCode, statusText, body) { var executor = Sys.Net.ScriptReferenceExecutor._executors[uniqueKey]; if (executor) { executor.complete(statusCode, statusText, body, true, false, false); } }
然后创建一个名为ScriptReferenceExecutor.ashx的一般处理程序
<%@ WebHandler Language="C#" Class="ScriptReferenceExecutor" %> using System; using System.Web; using System.Web.Script.Serialization; using System.Collections.Generic; public class ScriptReferenceExecutor : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/plain"; JavaScriptSerializer serializer = new JavaScriptSerializer(); Dictionary<string, string> headers = serializer.Deserialize<Dictionary<string, string>>(context.Request.QueryString["headers"]); string body = serializer.Deserialize<string>(context.Request.QueryString["body"]); string uniqueKey = serializer.Deserialize<string>(context.Request.QueryString["uniqueKey"]); string action = headers["action"]; if (action == "normal") { this.SendResponse(context, uniqueKey, 200, "OK", "You've send: " + body); } else if (action == "error") { this.SendResponse(context, uniqueKey, 500, "Error", null); } else { System.Threading.Thread.Sleep(5000); } } private void SendResponse(HttpContext context, string uniqueKey, int statusCode, string statusText, string body) { context.Response.Write("Sys.Net.ScriptReferenceExecutor.complete('" + uniqueKey + "', "); context.Response.Write("'" + statusCode + "', "); context.Response.Write("'" + statusText + "', "); context.Response.Write(new JavaScriptSerializer().Serialize(body) + ");"); } public bool IsReusable { get { return false; } } }
然后创建一个aspx页面,引入我们创建的js文件
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script language="javascript" type="text/javascript"> var webRequest=null; function sendRequest(action) { webRequest = new Sys.Net.WebRequest(); webRequest.set_url("ScriptReferenceExecutor.ashx"); //设置请求路径 webRequest.get_headers()["action"] = action;//设置一个key为action的Header webRequest.set_body("data=" + encodeURIComponent("Xiaoyaojian"));//设置发送的内容 webRequest.set_httpVerb("POST");//设置请求使用的HTTP方法 webRequest.set_timeout(3000);//设置超时时间 webRequest.set_executor(new Sys.Net.ScriptReferenceExecutor()); webRequest.add_completed(onCompleted);//添加完成时候的回调函数 webRequest.invoke();//执行请求 } function onCompleted(response, eventArgs) { if (response.get_aborted()) {//判断是否被取消 alert("Request aborted!"); } else if (response.get_responseAvailable()) {//判断得到的信息是否正确 var statusCode = response.get_statusCode();//得到状态码 if (statusCode < 200 || statusCode >= 300) {//状态码小于200或者大于等于300,则表示出现了错误 alert("Error occurred!"); } else { alert(response.get_responseData());//回复的信息 } } else { if (response.get_timedOut()) {//判断是否为超时 alert("Request timed out!"); } else { alert("Error occurred!"); } } } </script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Scripts> <asp:ScriptReference Path="~/Demo07/ScriptRerenceExcutor.js" /> </Scripts> </asp:ScriptManager> <input type="button" value="Normal" onclick="sendRequest('normal');" /> <input type="button" value="Error" onclick="sendRequest('error');" /> <input type="button" value="Time out" onclick="sendRequest('ad');" /> <input type="button" value="Abort" onclick="webRequest.get_executor().abort()" /> </form> </body> </html>
这样。我们就成功的使用了自定义的Executor