当前位置:首页 > Asp.Net Ajax 学习笔记14 扩展Microsoft AJAX Library的异步通信层

Asp.Net Ajax 学习笔记14 扩展Microsoft AJAX Library的异步通信层

点击次数:1416  更新日期:2010-12-29
\n

这一讲的内容主要是详细的了解Asp.Net Ajax异步通信层的实现方式。课程首先说明了各个类的作用


\n

WebRequest类:收集(存储)请求信息
WebRequestExecutor类:发送请求、反馈服务器端回复的结果
XMLHttpExecutor:封装了XMLHttpRequest的Executor
WebRequestManager类:用于管理异步通讯层与服务器端的通信
然后说明了一下异步通信层组件协作过程


\n

异步通信层组件协作过程
WebRequestManager确定Executor
触发WRM的invokingRequest事件
执行Executor的executeRequest方法
2. Executor返回、超时或被取消


\n

执行WebRequest的completed方法
触发WRM的completedRequest事件
触发WebRequest的complete事件
下面是使用Asp.Net Ajax类库的详细调用代码,我会将注释标明。


\n


\n

我们着重关注WebRequest的invoke调用


\n

function SysNetWebRequestinvoke()
{
//检查调用时是否存在参数,存在就抛出异常
if (arguments.length !== 0) throw Error.parameterCount();
//检查这个WebRequest是否已经被调用了,已经被调用了就抛出异常
if (this._invokeCalled)
{
throw Error.invalidOperation(Sys.Res.invokeCalledTwice);
}


\n

//使用WebRequestManager这个静态实例的方法executeRequest(执行请求),把当前执行invoke
//的对象作为参数,为什么说WebRequestManager是一个静态实例,因为在注册
//WebRequestManager类之后,有这样一段代码Sys.Net.WebRequestManager = new Sys.Net._WebRequestManager(),
//这说明Sys.Net命名空间里存在一个_WebRequestManager类型,在这个命名空间里有一个叫WebRequestManager实例,他的类型就是_WebRequestManager
Sys.Net.WebRequestManager.executeRequest(this);
//将已经调用标志设置为true
this._invokeCalled = true;
}


\n

上面这段代码,我们着重关注WebRequestManager的executeRequest(WebRequest request)方法


\n

function SysNet_WebRequestManagerexecuteRequest(webRequest)
{
//校验参数是否时WebRequest类型
var e = Function._validateParams(arguments, [{name: "webRequest", type: Sys.Net.WebRequest}]);
if (e) throw e;


\n

//得到webRequest的执行者对象
var executor = webRequest.get_executor();
//如果存在执行者,我们在客户端调用没有设置webRequest的执行者
//运行到这里应该进入这个if判断
if (!executor)
{
//如果WebRequest对象不存在执行者
var failed = false;
try
{
//得到WebRequestManger对象的默认执行者类型
var executorType = eval(this._defaultExecutorType);
//生成这个执行者对象,这个对象实际上就是XmlHttpExecutor
//XmlHttpExecutor实际上是继承与WebRequestExecutor类
executor = new executorType();
}
catch (e)
{
failed = true;
}


\n

//如果执行失败,或者执行者没有继承WebRequestExecutor类,就是说不是WebRequestExecutor实例
//或者executor为空,那么抛出异常
if (failed || !Sys.Net.WebRequestExecutor.isInstanceOfType(executor) || !executor)
{
throw Error.argument(“defaultExecutorType”, String.format(Sys.Res.invalidExecutorType, this._defaultExecutorType));
}


\n

//设置WebRequest的执行者
webRequest.set_executor(executor);
}


\n

//如果执行者取消了请求,那么就直接返回
if (executor.get_aborted())
{
return;
}


\n

//生成一个NetworkReuqestEventArgs参数对象,用webRequest最为参数
var evArgs = new Sys.Net.NetworkRequestEventArgs(webRequest);


\n

//得到invokingRequest事件的事件委托
var handler = this._get_eventHandlerList().getHandler(“invokingRequest”);
//如果存在这个事件委托,那么就调用这个事件委托
if (handler)
{
handler(this, evArgs);
}


\n

//如果外部事件委托执行没有取消执行
if (!evArgs.get_cancel())
{
//调用执行者executor的executeRequest()(执行请求方法)
executor.executeRequest();
}
}


\n


下面关注XmlHttpExecutor的executeRequest()方法


\n

function SysNetXMLHttpExecutorexecuteRequest()
{
//检测参数个数,如果不等于0,那么抛出异常
if (arguments.length !== 0) throw Error.parameterCount();


\n

//得到WebRequest对象,用来将保存的信息传递出去
//在WebRequest对象的set_executor方法里,已经将执行者对应的
//WebRequest对象设置到执行者中,并且set_executor方法是基类
//WebRequestExecutor类的方法,这里调用的是基类的方法
this._webRequest = this.get_webRequest();


\n

//判断这个执行者是否已经开始传递数据,如果已经开始传递,那么抛出异常
if (this._started)
{
throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, ‘executeRequest’));
}


\n

//判断webRequest对象是否为空,为空就抛出异常
if (this._webRequest === null)
{
throw Error.invalidOperation(Sys.Res.nullWebRequest);
}


\n

//得到WebReuqest对象的body
var body = this._webRequest.get_body();
//得到WebReuqest对象的header集合
var headers = this._webRequest.get_headers();
//这一段代码是标准的ajax应用
//实例化一个xmlHttpRequest对象
this._xmlHttpRequest = new XMLHttpRequest();


\n

//给定状态改变的回调函数
this._xmlHttpRequest.onreadystatechange = this._onReadyStateChange;
//得到WebReuqest对象的请求动作
var verb = this._webRequest.get_httpVerb();
//指定请求的连接和动作,并表明是一个异步传输
this._xmlHttpRequest.open(verb, this._webRequest.getResolvedUrl(), true );


\n

if (headers)
{
//遍历header集合
for (var header in headers)
{
var val = headers[header];
//如果header不是js函数
if (typeof(val) !== “function”)
//将header设置到xmlHttpRequest对象的请求头里
this._xmlHttpRequest.setRequestHeader(header, val);
}
}


\n

//判断动作是否是post
if (verb.toLowerCase() === “post”)
{
//通过设定的头,来设置传送内容的类型
if ((headers === null) || !headers[\'Content-Type\'])
{
this._xmlHttpRequest.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’);
}


\n

if (!body)
{
body = “”;
}
}


\n

//得到WebReuqest对象的超时时间
var timeout = this._webRequest.get_timeout();


\n

//如果设置了超时时间
if (timeout > 0)
{
//创建一个onTimeout函数的委托,并把XmlHttpExecutor对象作为this的上下文,
//并指定在超时时间后执行这个委托,将返回的对象放在_time内部成员里方便clearTimeout
this._timer = window.setTimeout(Function.createDelegate(this, this._onTimeout), timeout);
}


\n

this._xmlHttpRequest.send(body);


\n

//设置执行这已经开始标识
this._started = true;
}


\n



\n

在上面这个方法里,我们要关注3个方法


\n

第一个是_onReadyStateChange方法,这个方法将在ReadyState改变是被调用


\n

this._onReadyStateChange = function ()
{
//如果readState状态为4,表明成功返回
if (_this._xmlHttpRequest.readyState === 4 )
{
//清除超时时间
_this._clearTimer();
//设置响应状态为可用
_this._responseAvailable = true;
//告诉webRequest对象,我已经完成请求了
_this._webRequest.completed(Sys.EventArgs.Empty);


\n

//如果ajax异步传输对象不为空
if (_this._xmlHttpRequest != null)
{
//清除xmlHttpRequest的onreadystatechange委托
_this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
//清除ajax异步传输对象
_this._xmlHttpRequest = null;
}
}
}


\n



\n

第二个方法是_onTimeout,超时后执行的方法


\n

this._onTimeout = function this_onTimeout()
{
if (!_this._responseAvailable)
{
//清除超时时间设置的委托
_this._clearTimer();
//设置超时标志为true
_this._timedOut = true;
//清除xmlHttpRequest异步操作对象的onreadystatechange的回调函数
_this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
//取消xmlHttpRequest对象异步传输
_this._xmlHttpRequest.abort();
//告诉webRequest对象我已经完成操作
_this._webRequest.completed(Sys.EventArgs.Empty);
_this._xmlHttpRequest = null;
}
}


\n



\n

第三个是XMLHttpRequest的构造函数


\n

if (!window.XMLHttpRequest)
{
window.XMLHttpRequest = function windowXMLHttpRequest()
{
var progIDs = [ \'Msxml2.XMLHTTP\', \'Microsoft.XMLHTTP\' ];


\n

for (var i = 0; i < progIDs.length; i++)
{
try
{
var xmlHttp = new ActiveXObject(progIDs[i]);
return xmlHttp;
}
catch (ex)
{
}
}


\n

return null;
}
}


\n



\n

这是为了适应各种类型的浏览器,与我们原来创建标准的ajax异步传输例子相同


\n

我们还要关注WebRequest.completed方法


\n

function SysNetWebRequestcompleted(eventArgs)
{
//检测参数
var e = Function._validateParams(arguments, [{name: "eventArgs", type: Sys.EventArgs}]);
if (e) throw e;


\n

//得到WebRequestManger的completedRequest事件委托
var handler = Sys.Net.WebRequestManager._get_eventHandlerList().getHandler(“completedRequest”);
//如果存在这样的委托,那么调用这个委托,参数为执行者和args参数
if (handler)
{
handler(this._executor, eventArgs);
}


\n

//继续得WebRequest的completed事件委托,那么我们可以看到WebRequestManager的completedRequest事件是
//优先于WebRequest的completed事件的
handler = this._get_eventHandlerList().getHandler(“completed”);
//执行委托
if (handler)
{
handler(this._executor, eventArgs);
}
}


\n



\n

XmlHttpExecutor.abort()方法


\n

function SysNetXMLHttpExecutorabort()
{
//判断参数
if (arguments.length !== 0) throw Error.parameterCount();


\n

//如果执行者没有执行,抛出异常
if (!this._started)
{
throw Error.invalidOperation(Sys.Res.cannotAbortBeforeStart);
}


\n

//如果执行者的状态已经是abort,或者相应是无效的或者超时,那么直接返回
if (this._aborted || this._responseAvailable || this._timedOut)
return;


\n

//设置abort标志
this._aborted = true;


\n

//清除超时设置
this._clearTimer();


\n

//如果异步传输对象存在,并且还没有返回任何信息
if (this._xmlHttpRequest && !this._responseAvailable)
{
this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
this._xmlHttpRequest.abort();


\n

this._xmlHttpRequest = null;
//得到WebRequest对象的Completed事件委托
var handler = this._webRequest._get_eventHandlerList().getHandler(“completed”);


\n

if (handler)
{
handler(this, Sys.EventArgs.Empty);
}
}
}


\n



\n

我们可以看到除了超时,成功响应,错误,中断都会触发WebRequest的compeleted事件。


\n

下面的一个例子是模仿XmlHttpExecutor类,并且支持跨域名访问


\n

首先是ScriptReferenceExecutor类


\n

Sys.Net.ScriptReferenceExecutor = function()
{
//初始化基类
Sys.Net.ScriptReferenceExecutor.initializeBase(this);


\n

//设定各个字段的初始值
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;


\n

}


\n

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_xml : function()
{
return new XMLDOM(this.get_responseData());
},

//执行请求的方法
executeRequest : function()
{
//设置开始标记为true
this._started = true;

//得到WebRequest对象,就得到了保存在WebRequest中的各种值
var request = this.get_webRequest();
//得到序列化对象
var serializer = Sys.Serialization.JavaScriptSerializer;

//得到WebRequest对象请求的url地址
var scriptUrl = request.get_url() + “?”;
//组合头集合,并将头集合序列化,并且用Url转换字符
scriptUrl += ((“headers=”) + encodeURIComponent(serializer.serialize(request.get_headers())));
//组合body
scriptUrl += (“&body=” + encodeURIComponent(serializer.serialize(request.get_body())));

//得到一个执行者对象的唯一标识符,方便找到对应的执行者
var uniqueKey = this._uniqueKey = this._generateUniqueKey();
scriptUrl += (“&uniqueKey=” + encodeURIComponent(serializer.serialize(uniqueKey)));
//设置对应的执行者到执行者集合里
Sys.Net.ScriptReferenceExecutor._executors[uniqueKey] = this;

//创建脚本元素
var scriptElement = this._scriptElement = document.createElement(“script”);
scriptElement.type = “text/javascript”;
scriptElement.language = “javascript”;
//将脚本元素的src设置为组合好的url
scriptElement.src = scriptUrl;
//得到页面的head元素,因为head没有ID,只能通过TagName得到,然后将
//脚本元素追加到头
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();
},

//执行者的完成函数参数为状态代码、状态描述、body、是否返回成功、是否超时、是否中断操作
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);
}
//移除已经提交给服务气的脚本元素,也就是url
document.getElementsByTagName(“head”)[0].removeChild(this._scriptElement);
//删除已经执行的执行者
delete Sys.Net.ScriptReferenceExecutor._executors[this._uniqueKey];

//调用WebRequest的completed方法
this.get_webRequest().completed(Sys.EventArgs.Empty);
}
}
//注册类
Sys.Net.ScriptReferenceExecutor.registerClass(“Sys.Net.ScriptReferenceExecutor”, Sys.Net.WebRequestExecutor);
Sys.Net.ScriptReferenceExecutor._executors = new Object();
//执行者的静态complete方法
Sys.Net.ScriptReferenceExecutor.complete = function(uniqueKey, statusCode, statusText, body)
{
//判断执行者是否存在
var executor = Sys.Net.ScriptReferenceExecutor._executors[uniqueKey];
if (executor)
{
//调用执行者对象的complete方法。
executor.complete(statusCode, statusText, body, true, false, false);
}
}


\n

再是接受请求的服务端


\n

<%@ WebHandler Language=”C#” Class=”ScriptReferenceExecutor” %>


\n

using System;
using System.Web;
using System.Collections.Generic;
using System.Web.Script.Serialization;


\n

public class ScriptReferenceExecutor : IHttpHandler {

public void ProcessRequest (HttpContext context)
{
context.Response.ContentType = “text/plain”;


\n

// context.Response.Write(“alert(‘Message Received!’);”);


\n

//得到序列化对象
JavaScriptSerializer serializer = new JavaScriptSerializer();
//反序列化头集合为字典集合
Dictionary<string, string> headers = serializer.Deserialize<Dictionary<string, string>>(
context.Request.QueryString["headers"]);
//反序列化body为字符串
string body = serializer.Deserialize<string>(context.Request.QueryString["body"]);
//反序列化执行者对象的标识
string uniqueKey = serializer.Deserialize<string>(context.Request.QueryString["uniqueKey"]);


\n

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);
}
}


\n

//发送响应信息
private void SendResponse(HttpContext context, string uniqueKey, int statusCode, string statusText, string body)
{
//调用ScriptReferenceExecutor的静态方法complete
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;
}
}


\n

}
剩下的就是页面使用


\n

<%@ Page Language=”C#” %>


\n

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>


\n

<script runat=”server”>


\n

</script>


\n

<html xmlns=”http://www.w3.org/1999/xhtml” >
<head runat=”server”>
<title>Use Async Communication Layer</title>

<script language=”javascript” type=”text/javascript”>
var webRequest = null;

function sendRequest(action)
{
webRequest = new Sys.Net.WebRequest();
webRequest.set_url(“Handlers/ScriptReferenceExecutor.ashx”);
webRequest.get_headers()["action"] = action;
webRequest.set_body(“Hello World!”);
webRequest.set_timeout(3000);

//设置webRequest的执行者
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))
{
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” ScriptMode=”Debug”>
<Scripts>
<asp:ScriptReference Path=”ScriptReferenceExecutor.js” />
</Scripts>
</asp:ScriptManager>

<input type=”button” value=”Normal” onclick=”sendRequest(‘normal’);” />
<input type=”button” value=”Error” onclick=”sendRequest(‘error’);” />
<input type=”button” value=”Timeout” onclick=”sendRequest(‘abc’)” />
<input type=”button” value=”Abort” onclick=”webRequest.get_executor().abort()” />
</form>
</body>
</html>


\n

这就是继承并扩展WebRequestExecutor类


\n

\n