• 转 Ajax.NET源码分析


    http://www.cnblogs.com/dingsea/archive/2006/02/20/333730.html
    现在仿佛比较流行AJAX了。05年底从网上下了一个Ajax.NET在项目中应用过,感觉不错,来的方便量又足:)。结果就一直放在一边没有管它了。这几天心血来潮拿出来看它的源代码。如果有想要的朋友,请从这儿下载

     

    相关网站:http://ajax.schwarz-interactive.de/csharpsample/default.aspx

     

    打开AJAX源代码一看,一个解决方案里就一个项目,生成后也是一个DLL,很方便。

    在分析源代码之前建议大家使用一下它,具体的应用方法在上边的链接已经能很清楚的找到,如果想要看中文的帮助文档,请访问这位老兄的文章:

    http://herony420.cnblogs.com/archive/2006/02/16/331949.html

    写的很完善。

     

    前置条件:了解AJAX的大概运作机制。可以参考ASP实现XMLHttpRequest

     

    好,现在我们就从粗线条入手,大概地了解一下这个library是怎么运作的。

    上图是AJAX的项目文件组织情况。

     

    基本思路:

    1、  配置好web.config后,按正常情况加载页面;

    2、  加载页面的page_loag事件中注册AJAX

    3、  页面加载完成后马上产生第一次请求,注意,这不是一个xmlHttpRequest

    4、  服务端实例化关于XmlHttpRequest和其它一些要使用的Javascript代码,发送到客户端来。

    5、  客户事件可以利用异步请求进行工作了。这些请求会被web.config中指定的类intercept掉,服务端处理相关请求,返回结果。

     

    配置web.config

    具体内容如下:

    <httpHandlers>

                         
    <add verb="POST,GET" path="ajax/*.ashx" type="BorgWorX.Web.Core.Ajax.PageHandlerFactory, BorgWorX.Web.Core.Ajax"/>

            
    </httpHandlers>



    关于ashx文件,网上的资料不是很多,能用的就更少。它在这儿的作用是让BorgWorX.Web.Core.Ajax.PageHandlerFactory类返回一个IHttpHandler并处理之。当向服务器请求时,都会被IHttpHandlerFactory拦下来。

    page_load事件中,有一段代码:

    BorgWorX.Web.Core.Ajax.Utility.RegisterTypeForAjax(typeof(WebForm1));

    主要作用是向客户端输出一段JS

    <script type="text/javascript" src="/ajaxTest/ajax/common.ashx"></script>

    <script type="text/javascript" src="/ajaxTest/ajax/ajaxTest.WebForm1,App_Web_6jqhtozc.ashx"></script>

    客户端执行这段代码后,就开始向服务器请求ajax/common.ashx这个文件了,但不会被请求到,因为在被PageHandlerFactory拦截后,服务器就已经根据情况(主要是看context.Request[ "_session" ]的值)返回不同的IHttpHandler

    第一次请求时返回的是一个AjaxHandlerSessionStateR,它的构造函数直接使用的是它的基类AjaxHandlerAjaxHandler重写了ProcessRequest方法,于是此方法执行:

    AjaxRequestProcessor proc = new AjaxRequestProcessor( context );

    proc.Run();

    在第一次请求中,proc.Run();是关键,它向客户端输入了很多javascript代码,包括产生异步调用的代码和客户端的“DataSet”等。

    异步调用的代码,是放在Ajax.JS文件里的,在proc.Run();的时候用AjaxProcessorRenderCommonScript

    /**//// <summary>

          
    /// Render common script to the Response.

          
    /// </summary>

          
    internal void RenderCommonScript() {

                
    // We have to use different common javascript source for

                
    // Windows Mobile PCs and all other browsers (IE, Firefox, Safari, Netscape, Opera)

     

                
    string commonAjax = "ajax.js";

     

                
    if ( context.Request.UserAgent.IndexOf( "Windows CE; PPC;" ) >= 0 ) {

                      commonAjax 
    = "ajax_mobile.js";

                }


     

                StreamReader sr 
    = new StreamReader( Assembly.GetCallingAssembly().GetManifestResourceStream( "BorgWorX.Web.Core.Ajax." + commonAjax ) );

                Write( sr.ReadToEnd() );

                sr.Close();

     

                Write( 
    "var ajaxVersion = '5.7.22.2';" );

          }

     

    此处因为Ajax.JS是内嵌资源,所以用GetManifestResourceStream来进行读取并输出,而且这些输出是在客户端发送request后的,所以我们通过右键看源代码也看不到。

    当客户端把response出来的JS加载完后,第一次页面的loading才算完成。

     

    对于已经加载完毕的页面发送的异步请求,比如返回两个整数相加的值(函数签名为public int ServerSideAdd(int firstNumber, int secondNumber)Ajax.NET是这么处理的:

    首先在客户端异步向服务器post,服务器这边由PageHandlerFactory拦截,并分析context.Request[ "_session" ]的值,通过分析发现该值匹配context.Request[ "_session" ] == "no",于是执行return new AjaxHandler();随后fire AjaxHandlerProcessRequest

    public override void ProcessRequest( HttpContext context ) {

                AjaxRequestProcessor proc 
    = new AjaxRequestProcessor( context );

     

                proc.Run();

          }

    这儿我们应该可以猜到所有的事都交给Run来完成了。好,我们深入进去看看。

    Run的代码里有这么一句:

    Line 49:

    DateTime now = DateTime.Now;

    也许有人奇怪这是为什么,还加个时间,其实这个时间在后边作缓存时是有用的。

     

    Line 57:

    AjaxProcessor ajaxer = new AjaxProcessor();

    ajaxer.InitializeContext( 
    ref context );

    实例化一个AjaxProcessor,后边有用。

     Line 64:

    if ( typeName.ToLower() == "common" )

    此时为false,所以不用再执行方法ajaxer.RenderCommonScript();(line 72)了。

     

    Line 87:

    Type type;

    MethodInfo[] mi 
    = null;

    string methodName = null;

    object[] po = new object[] {};

    string md5 = null;

    StreamReader sr 
    = null;

     准备反射和内存流读取。

    下图为type的实例化:

     

    Line 99:

    byte[] b = new byte[context.Request.ContentLength];

    设置读取的buffer

     Line 100:

    if ( context.Request.HttpMethod == "POST" && context.Request.InputStream.Read( b, 0, b.Length ) >= 0 )

    此时返回为true,以下代码被执行。

     Line 101:

    md5 = MD5Helper.GetHash( b );

    返回的md5是一串排列好了的hash codeMD5Helper的这个方法封装了MD5CryptoServiceProviderComputeHash方法,在后边会把这个md5和刚才我们得到的时间一起在缓存时使用。

     Line 102:

    sr = new StreamReader( new MemoryStream( b ), UTF8Encoding.UTF8 );

    开始流的读取。

     

    Line 104:


    if ( context.Cache[ type.FullName + "|" + md5 ] != null ) {

                           context.Response.Write( context.Cache[ type.FullName 
    + "|" + md5 ].ToString() );

    }

    如果允许缓存,则直接使用之,它好我也好。

     Line 119:

    MethodInfo method = type.GetMethod( methodName );

     

                      
    if ( method != null ) {

                           
    object[] ii = method.GetCustomAttributes( typeof (AjaxMethodAttribute), true );

     

                           
    if ( ii.Length != 1 ) {

                                 
    // the method does not have one AjaxMethodAttribute

     

                                 ajaxer.HandleException( 
    new NotImplementedException( "The method '" + context.Request[ "m" ] + "' is not implemented or access refused." ), null );

                                 
    if ( context.Trace.IsEnabled ) {

                                      context.Trace.Write( 
    "Ajax.NET""End ProcessRequest" );

                                 }


     

                                 
    return;

                           }

    开始反射。

    如图:

    记的我们在使用AJAXNET的时候要在公有方法前加一个

    [BorgWorX.Web.Core.Ajax.AjaxMethod()]这样的属性吗?现在有用了。不过ii.Length != 1将会为false,所以不会return,代码继续往下走。

     Line 139

    ParameterInfo[] para = method.GetParameters();

    po 
    = new object[para.Length];

    try {

    // the AjaxProcessor will read the values either form the 

    // request URL or the request input stream.

    ajaxer.RetreiveParameters( 
    ref sr, para, ref po );

    继续反射,RetreiveParameters最后会让para去填充po的。现在po可以直接使用了。此方法中代码会检查context.Request[ "_return" ] != null && context.Request[ "_return" ] == "xml"并实例化一个hashtable,通过sr.ReadLine();把传入的两个参数先进行replace处理,主要是%=号,然后通过字符串操作把=号剥离并放入hashtable中,两个参数一共放两次。最后将hashtable中的两个object转化为正确的类型。

    Line 156:

    object o = null;

    o是用来放结果的。

     Line 191:

    object c = (object) Activator.CreateInstance( Type.GetType( typeName ), new object[] {} );
    if ( c != null ) {
    = method.Invoke( c, po );
    }

    利用反射创建一个page类,并调用其方法,参数是刚才存入hashtable中的值。

     

    最后,向客户端response 输o,异步调用完毕。

    Ajax.NET引入了JSON(JavaScript Object Notation),具体请参考http://www.json.org/

    下图是JSON的使用情况:

     

    小结:

    AJAX.NET的创意点:

    1、             Ajax.js文件的嵌入;

    2、             异步调用的服务器端处理,没有涉及到页面,较好的复用性;

    3、             使用反射直接计算值(但不知道performance test表现如何);

    4、             缓存机制;

    5、             整合JSON,方便编程。

     

     

  • 相关阅读:
    linux 下使用scp命令传输文件
    yii2使用vendor文件夹下的的css文件
    yii2中使用定义在 params.php文件中的配置
    PHP Catchable fatal error: Argument 2 passed to IlluminateRoutingUrlGenerator::__construct()
    git 去除本地所有没有保存的修改
    学习修复Laravel The only supported ciphers are AES-128-CBC and AES-256-CBC
    [190308]Ubuntu 安装完之后,安装的软件小记
    swagger.yaml转换为swagger.json文件
    Linux sed -i 字符串替换
    [笔记]Laravel TDD 胡乱记录
  • 原文地址:https://www.cnblogs.com/flyfish/p/340033.html
Copyright © 2020-2023  润新知