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

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

  1. WebRequest类:收集(存储)请求信息
  2. WebRequestExecutor类:发送请求、反馈服务器端回复的结果
  3. XMLHttpExecutor:封装了XMLHttpRequest的Executor
  4. WebRequestManager类:用于管理异步通讯层与服务器端的通信

然后说明了一下异步通信层组件协作过程

  1. 异步通信层组件协作过程
  • WebRequestManager确定Executor
  • 触发WRM的invokingRequest事件
  • 执行Executor的executeRequest方法

    2.   Executor返回、超时或被取消

  • 执行WebRequest的completed方法
  • 触发WRM的completedRequest事件
  • 触发WebRequest的complete事件

下面是使用Asp.Net Ajax类库的详细调用代码,我会将注释标明。

<script language="javascript" type="text/javascript">
function sendRequest()
...{
debugger;
//生成一个WebRequest对象,用来保存请求的信息
var request = new Sys.Net.WebRequest();
//设置处理的地址
            request.set_url("Handlers/Simple.ashx");
//设置处理的动作
            request.set_httpVerb("POST");           
//设置完成后的回调函数
            request.add_completed(onCompleted);
//调用请求
            request.invoke();
        }

function onCompleted(response, eventArgs)
...{
debugger;
            alert(response.get_responseData());
        }
</script>

我们着重关注WebRequest的invoke调用

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


    
//使用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;
}
 

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

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

    
//得到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;
        }


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


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


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


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

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


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

}
 

下面关注XmlHttpExecutor的executeRequest()方法

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

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

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


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


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

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

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

    }


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


        
if (!body) 
        
{
            body 
= "";
        }

    }


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

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


    
this._xmlHttpRequest.send(body);

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

 

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

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

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

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

    }

}
 

 

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

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

}
 

 

第三个是XMLHttpRequest的构造函数

if  ( ! window.XMLHttpRequest) 
{
    window.XMLHttpRequest 
= function window$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;
    }

}
 

 

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

我们还要关注WebRequest.completed方法

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

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


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

}
 

 

XmlHttpExecutor.abort()方法

function  Sys$Net$XMLHttpExecutor$abort() 
{
    
//判断参数
    if (arguments.length !== 0throw Error.parameterCount();

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


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

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

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

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

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

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

    }

}
 

 

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

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

首先是ScriptReferenceExecutor类

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_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(
thisthis._onTimeout), timeout);
        }

        
    }
,
    
    _onTimeout : 
function()
    
{
        
this.complete(nullnullnullfalsetruefalse);
    }
,
    
    abort : 
function()
    
{
        
this.complete(nullnullnullfalsefalsetrue);
    }
,
    
    _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, truefalsefalse);
    }

}

 

再是接受请求的服务端

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

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

public   class  ScriptReferenceExecutor : IHttpHandler  {
    
    
public void ProcessRequest (HttpContext context) 
    
{
        context.Response.ContentType 
= "text/plain";

        
// context.Response.Write("alert('Message Received!');");

        
//得到序列化对象
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        
//反序列化头集合为字典集合
        Dictionary<stringstring> headers = serializer.Deserialize<Dictionary<stringstring>>(
            context.Request.QueryString[
"headers"]);
        
//反序列化body为字符串
        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)
    
{
        
//调用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;
        }

    }


}

剩下的就是页面使用

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

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

< script  runat ="server" >

</ script >

< 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 >

这就是继承并扩展WebRequestExecutor类

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值