深入Atlas系列:客户端网络访问基础结构(下) - WebRequestExecutor和XMLHttpExecutor

  正如前一篇文章所说的那样, WebRequestExecutor是客户端网络访问的基础结构的唯一扩展点,而XMLHttpExecutor是其默认实现。在ASP.NET AJAX中,开发人员能够将自定义的WebRequestExecutor子类设为默认的Executor,也可以为某一个WebRequest指定一个 特定的Executor。虽然一般来说XMLHttpExecutor已经足够大多数应用,但是既然ASP.NET AJAX提供了这个功能,我们也根据默认的类进行一下这方面的学习。


WebRequestExecutor

  在ASP.NET AJAX客户端脚本中,Sys.Net.WebRequestExecutor是一个抽象类,它有以下成员:
  • webRequest只读属性:这个属性已经在Sys.Net.WebRequestExecutor得到定义,一般不用去关心它。
  • started只读属性:抽象属性,表明该Executor是否已经开始执行了(或执行过了)。
  • responseAvailable只读属性:抽象属性,表明该Executor是否已经得到response了。
  • timeOut只读属性:抽象属性,表明该Executor是否已经超时。
  • aborted只读属性:抽象属性,表明该Executor是否已经被取消。
  • responseData只读属性:抽象属性,返回字符串形式的response内容。
  • statusCode只读属性:抽象属性,返回请求结果的状态代码。
  • statusText只读属性:抽象属性,返回字符串形式表示的请求结果状态。
  • xml只读属性:抽象属性,返回xml形式的response内容。
  • object只读属性:这个属性已经在Sys.Net.WebRequestExecutor得到定义,用于返回一个表请求明结果的对象。
  • executeRequest方法:抽象方法,用于执行当前的请求。
  • abort方法:抽象方法,用于取消当前的请求。
  • getResponseHeader方法:抽象方法,通过指定的name,从请求结果的Header中获得对应的值。
  • getAllResponseHeaders方法:抽象方法,返回字符串形式表示的所有的Header信息。
  大多数朋友们应该可以发现上面的许多方法或属性都和XMLHttpRequest对象的方法或属性一一对应,我对于上面这些方法和属性的理解也是如 此。但是事实上,大部分的方法和属性由于都是抽象的,因此从实现上完全可以由开发人员自由发挥。还有关键的一点就是返回值的类型也可以改变。例如,在 XMLHttpRequest对象中,getAllResponseHeaders方法的作用是返回一个字符串,用来表示整个Response的 Header,如果我们在开发自定义的WebRequestExecutor时完全可以使其返回一个对象,这样从使用角度来说也会比较方便。

  WebRequestExecutor很简单,接下来我们从ASP.NET AJAX中的默认实现来自己看一下具体的实现方式。


XMLHttpExecutor

  大部分的属性与方法的意义实在不大。我们就来看几个关键方法的实现吧,它们会涉及到WebRequestExecutor的状态,即使我们的实现不同,这些状态一般来说还是保持统一的。

1、构造函数

  构造函数的作用其实只是初始化所有的状态,它们分别是:
  • responseAvailable属性设为false
  • timedOut属性设为false
  • aborted属性设为false
  • started属性设为false

2、executeRequest方法

  调用这个方法就会构造一个XMLHttpRequest对象,并发起一个请求。代码如下:
Sys.Net.XMLHttpRequest.executeRequest方法
 1 function Sys$Net$XMLHttpExecutor$executeRequest() {
 2     if (arguments.length !== 0throw Error.parameterCount();
 3 
 4     // 通过get_webRequest方法获得当前的WebRequest,
 5     // 这个是Sys.Net.WebRequestExecutor的方法。
 6     this._webRequest = this.get_webRequest();
 7 
 8     // 如果已经开始过了,那么抛出异常
 9     if (this._started) {
10         throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'executeRequest'));
11     }
12 
13     // 如果没有指定WebRequest,也会抛出异常。
14     if (this._webRequest === null) {
15         throw Error.invalidOperation(Sys.Res.nullWebRequest);
16     }
17 
18     // 构造XMLHttpRequest对象并设定各项值
19     var body = this._webRequest.get_body();
20     var headers = this._webRequest.get_headers();
21     this._xmlHttpRequest = new XMLHttpRequest();
22     this._xmlHttpRequest.onreadystatechange = this._onReadyStateChange;
23     var verb = this._webRequest.get_httpVerb();
24     this._xmlHttpRequest.open(verb, this._webRequest.getResolvedUrl(), true );
25 
26     // 添加header
27     if (headers) {
28         for (var header in headers) {
29             var val = headers[header];
30             if (typeof(val) !== "function")
31                 this._xmlHttpRequest.setRequestHeader(header, val);
32         }
33     }
34 
35     // 如果使用POST方法
36     if (verb.toLowerCase() === "post") {
37         // 则需要设置Content-Type
38         if ((headers === null|| !headers['Content-Type']) {
39             this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
40         }
41         if (!body) {
42             body = "";
43         }
44     }
45 
46     // 如果指定了timeout时间
47     var timeout = this._webRequest.get_timeout();
48     if (timeout > 0) {
49         // 则取消
50         this._timer = window.setTimeout(Function.createDelegate(thisthis._onTimeout), timeout);
51     }
52 
53     this._xmlHttpRequest.send(body);
54     // 表明已经开始了
55     this._started = true;
56 }

  可以发现,在这个方法被调用之后,started属性被设为了true,以此避免同一个Executor被执行两次。

3、_onReadyStateChange方法

  这个方法是XMLHttpRequest对象的onreadystatechange回调函数。代码如下:
_onReadyStateChange方法
 1 this._onReadyStateChange = function () {
 2     // 当xmlHttpRequest执行完毕后    
 3     if (_this._xmlHttpRequest.readyState === 4 ) {
 4 
 5         // 清除timeout定时器
 6         _this._clearTimer();
 7 
 8         // 将responseAvailable属性设为true
 9         _this._responseAvailable = true;
10 
11         // 触发WebRequestManager的completedRequest事件和WebRequest的completed事件
12         _this._webRequest.completed(Sys.EventArgs.Empty);
13 
14         // 释放XMLHttpRequest对象
15         if (_this._xmlHttpRequest != null) {
16             _this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
17             _this._xmlHttpRequest = null;
18         }
19     }
20 }

  在这个方法里,只会判断XMLHttpRequest对象的readyState的值,等于4则表示收到了回复,已经执行完毕了,然后进入其 他逻辑。这个和“标准”的AJAX逻辑似乎有些不同,这是因为在ASP.NET AJAX中的WebRequestExecutor中,会将判断response结果的逻辑交由其它代码完成。在Executor中只需表示 “Complete”即可,其余的,都交给别人去吧。

  在这个方法中,responseAvailable被设为了true,表示已经得到了Response――其实在ASP.NET AJAX这部分模型中,这个Response对象就是当前的WebRequestExectuor自身。

4、_onTimeout方法

  timeout触发器会调用这个方法。代码如下:
_timeout方法
 1 this._onTimeout = function this$_onTimeout() {
 2     // 可能在timeout触发时已经获得结果了,
 3     // 排出这种状况
 4     if (!_this._responseAvailable) {
 5         // 清除timeout触发器
 6         _this._clearTimer();
 7         // timeout设为true
 8         _this._timedOut = true;
 9         // 取消XMLHttpRequest所用的回调函数
10         _this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
11         // 取消XMLHttpRequest对象的操作
12         _this._xmlHttpRequest.abort();
13         // 触发WebRequest的completed事件
14         _this._webRequest.completed(Sys.EventArgs.Empty);
15         // 释放XMLHttpRequest对象
16         _this._xmlHttpRequest = null;
17     }
18 }

  当一个请求超时的时候,需要调用XMLHttpRequest.abort方法来取消这个请求。同时,timeout属性被设为了true。

5、abort方法

  这个方法负责取消一个请求。代码如下:
abort方法
 1 function Sys$Net$XMLHttpExecutor$abort() {
 2     if (arguments.length !== 0throw Error.parameterCount();
 3 
 4     // 如果还没有开始,则抛出异常
 5     if (!this._started) {
 6         throw Error.invalidOperation(Sys.Res.cannotAbortBeforeStart);
 7     }
 8 
 9     // 如果已经被取消了或者已经超时了,则返回
10     if (this._aborted || this._responseAvailable || this._timedOut)
11         return;
12 
13     // 将abort属性设为true
14     this._aborted = true;
15 
16     // 清除超时触发器
17     this._clearTimer();
18 
19     // 保险起见,需要判断是否已经执行完毕了
20     if (this._xmlHttpRequest && !this._responseAvailable) {
21 
22         this._xmlHttpRequest.onreadystatechange = Function.emptyMethod;
23         this._xmlHttpRequest.abort();
24 
25         this._xmlHttpRequest = null;
26         var handler = this._webRequest._get_eventHandlerList().getHandler("completed");
27         if (handler) {
28             handler(this, Sys.EventArgs.Empty);
29         }
30     }
31 }

  这个方法会调用XMLHttpRequest对象的abort方法来取消这个请求,并且将aborted属性设为true。


   上面就是XMLHttpExecutor类的“分析”了,其实代码都非常简单(有时候我会想,难道我是为了系列文章内容的完整性才写这些吗?它们的质量 有没有到一定的标准?它们有一定的存在价值吗?)。我们来思考一个问题,到底什么时候我们需要自定义一个Executor?我们需要满足一个什么样的功 能?在实际应用中XMLHttpRequest的功能还不够吗?写一个IFrameExecutor?个人认为,一般来说,如果真的要对 WebRequestExecutor进行扩展的话,估计也是在XMLHttpExecutor的基础上进行扩展了,我们可以依靠对于 XMLHttpExecutor的继承或者封装附加一些新的功能,例如统一的身份验证和异常处理等等。在与这篇“分析”相对应的“示例”中,我将提供一个 继承于XMLHttpExecutor的类,它能够将页面上所有请求的全部或部分信息输出在页面上,方便开发人员跟踪与调试。当然,这只是一个在开发过程 中的应用。至于完整定义一个WebRequestExecutor……那么为客户端的单元测试定义一个WebRequestExecutor的mock对 象吧。

  感觉“深入Atlas系列”的文章有些难写了,有时候我也会忽然不知道应该写什么。希望大家对我的文章多提一些意见和建议。谢谢大家!

本文出自 “赵��” 博客,转载请与作者联系!

你可能感兴趣的:(客户端,休闲,Atlas,XMLHttpExecutor)