Ajax框架原理分析之AjaxPro

  最近工作比较闲,可以静下心来研究自己感兴趣的问题.AjaxPro框架是一个有点历史的.net上的Ajax框架了,使用起来非常方便,一直想了解它究竟是怎么实现的,现在终于有时间啦!

前台代码:

代码
< body >
    
< form  id ="form1"  runat ="server" >
    
< asp:TextBox  ID ="txtData"  runat ="server" ></ asp:TextBox >
    
< input  type ="button"  onclick ="getdate()"  value ="aaaa"   />
    
</ form >

    
< script >
        
function  getdate() {
            _Default.GetData(
function () { alert( 0 ); });
        }
    
</ script >
</ body >

 

后台代码:

代码
public   partial   class  _Default : System.Web.UI.Page
{
    
protected   void  Page_Load( object  sender, EventArgs e)
    {
        AjaxPro.Utility.RegisterTypeForAjax(
typeof (_Default));
    }

    [AjaxPro.AjaxMethod()]
    
public   void  GetData() { }
}


在后台注册本页面后,会在本页面的HTML代码的顶部自动生成四个的引用标签,其中前三个为引用AjaxPro库文件,第四个会跟据不同的页面按照相同的模板生成不同名称的不同内容的JS文件

代码
< script  type ="text/javascript"  src ="/Web/ajaxpro/prototype.ashx" ></ script >
< script  type ="text/javascript"  src ="/Web/ajaxpro/core.ashx" ></ script >
< script  type ="text/javascript"  src ="/Web/ajaxpro/converter.ashx" ></ script >
< script  type ="text/javascript"  src ="/Web/ajaxpro/_Default,App_Web_dgtqp_pl.ashx" ></ script >


自动生成的脚本,可以看到,前台也会生成与后台页面类_Default同名的的JS类,前且也有个同名方法GetData:

代码
_Default_class  =   function () {};
Object.extend(_Default_class.prototype, Object.extend(
new  AjaxPro.AjaxClass(), {
    GetData: 
function () {
        
return   this .invoke( " GetData " , {},  this .GetData.getArguments().slice( 0 ));
    },
    url: 
' /Web/ajaxpro/_Default,App_Web_dgtqp_pl.ashx '
}));
_Default 
=   new  _Default_class();


调动的过程:

1.客户端调用_Default.GetData(function() { alert(0); });

  实质是调用_Default类的GetData方法,这个类在上面第四个引用的文件内

2.调用this.invoke("GetData", {}, this.GetData.getArguments().slice(0));

  可以看到,this,也就是_Default类"继承"AjaxPro.AjaxClass()类,可以从core.ashx的JS脚本中看到,其实后者的invoke方法调用了AjaxPro.Request()类的invoke方法.

  在这里,第一个参数为方法名,第二个参数为后台方法参数,第三个方法实际上为第一步JS调用的最后一个参数,可能什么也没有,也可能为一个后台参数,也可能为一个JS回调函数.


 AjaxPro.AjaxClass()类的invoke方法:

代码
invoke:  function (method, args, e) {

    
if  (e  !=   null ) {
        
if  (e.length  !=   6 ) {
            
for  (; e.length  <   6 ; ) { e.push( null ); }
        }
        
if  (e[ 0 !=   null   &&   typeof  (e[ 0 ])  ==   " function " ) {
            
return  AjaxPro.queue.add( this .url, method, args, e);
        }
    }
    
var  r  =   new  AjaxPro.Request();
    r.url 
=   this .url;
    
return  r.invoke(method, args);
}

 

 AjaxPro.Request()类的invoke方法:

代码
invoke:  function (method, args, callback, context) {
    
this .__start  =   new  Date().getTime();

    
//  if(this.xmlHttp == null) {
     this .xmlHttp  =   new  XMLHttpRequest();
    
//  }

    
this .isRunning  =   true ;
    
this .method  =  method;
    
this .args  =  args;
    
this .callback  =  callback;
    
this .context  =  context;

    
var  async  =   typeof  (callback)  ==   " function "   &&  callback  !=  AjaxPro.noOperation;

    
if  (async) {
        
if  (MS.Browser.isIE) {
            
this .xmlHttp.onreadystatechange  =   this .doStateChange.bind( this );
        } 
else  {
            
this .xmlHttp.onload  =   this .doStateChange.bind( this );
            
this .xmlHttp.onerror  =   this .mozerror.bind( this );
        }
        
this .onLoading( true );
    }

    
var  json  =  AjaxPro.toJSON(args)  +   "" ;
    
if  (AjaxPro.cryptProvider  !=   null   &&   typeof  AjaxPro.cryptProvider.encrypt  ==   " function " ) {
        json 
=  AjaxPro.cryptProvider.encrypt(json);
    }

    
this .xmlHttp.open( " POST " this .url, async);
    
this .xmlHttp.setRequestHeader( " Content-Type " " text/plain; charset=utf-8 " );
    
this .xmlHttp.setRequestHeader( " X- "   +  AjaxPro.ID  +   " -Method " , method);

    
if  (AjaxPro.token  !=   null   &&  AjaxPro.token.length  >   0 ) {
        
this .xmlHttp.setRequestHeader( " X- "   +  AjaxPro.ID  +   " -Token " , AjaxPro.token);
    }

    
/*  if(!MS.Browser.isIE) {
    this.xmlHttp.setRequestHeader("Connection", "close");
    } 
*/

    
this .timeoutTimer  =  setTimeout( this .timeout.bind( this ), AjaxPro.timeoutPeriod);

    
try  {  this .xmlHttp.send(json); }  catch  (e) { }  //  IE offline exception

    
if  ( ! async) {
        
return   this .createResponse({ error:  null , value:  null  });
    }

    
return   true ;
}


3.如果第一步的JS调用有回调函数,则执行return AjaxPro.queue.add(this.url, method, args, e),否则执行return r.invoke(method, args);

  如果没有回调函数,则不会为this.xmlHttp.onreadystatechange设置回调.  

  如果有回调函数,它会将请求放入AjaxPro.queue队列中.AjaxPro.queue队列是AjaxPro.RequestQueue类,里面有个AjaxPro.Request类型的数组,表示可以并发请求.在AjaxPro.RequestQueue类的内部循环生成AjaxPro.Request类型的数组的时候,为每个AjaxPro.Request类型设置了callback方法.

4.这时请求到了服务端.由AjaxHandlerFactory类接收.

代码
public   class  AjaxHandlerFactory : IHttpHandlerFactory
{
    
//  Methods
     public  IHttpHandler GetHandler(HttpContext context,  string  requestType,  string  url,  string  pathTranslated)
    {
        
string  fileNameWithoutExtension  =  Path.GetFileNameWithoutExtension(context.Request.Path);
        Type type 
=   null ;
        Exception exception 
=   null ;
        
bool  flag  =   false ;
        
try
        {
            
if  ((Utility.Settings  !=   null &&  Utility.Settings.UrlNamespaceMappings.Contains(fileNameWithoutExtension))
            {
                flag 
=   true ;
                type 
=  Type.GetType(Utility.Settings.UrlNamespaceMappings[fileNameWithoutExtension].ToString(),  true );
            }
            
if  (type  ==   null )
            {
                type 
=  Type.GetType(fileNameWithoutExtension,  true );
            }
        }
        
catch  (Exception exception2)
        {
            exception 
=  exception2;
        }
        
string  str2  =  requestType;
        
if  (str2  !=   null )
        {
            
if  ( ! (str2  ==   " GET " ))
            {
                
if  (str2  ==   " POST " )
                {
                    
if  ( ! ( ! Utility.Settings.OnlyAllowTypesInList  ||  flag))
                    {
                        
return   null ;
                    }
                    IAjaxProcessor[] processorArray 
=   new  IAjaxProcessor[] {  new  XmlHttpRequestProcessor(context, type),  new  IFrameProcessor(context, type) };
                    
for  ( int  i  =   0 ; i  <  processorArray.Length; i ++ )
                    {
                        
if  (processorArray[i].CanHandleRequest)
                        {
                            
if  (exception  !=   null )
                            {
                                processorArray[i].SerializeObject(
new  NotSupportedException( " This method is either not marked with an AjaxMethod or is not available. " ));
                                
return   null ;
                            }
                            AjaxMethodAttribute[] customAttributes 
=  (AjaxMethodAttribute[]) processorArray[i].AjaxMethod.GetCustomAttributes( typeof (AjaxMethodAttribute),  true );
                            
bool  useAsyncProcessing  =   false ;
                            HttpSessionStateRequirement readWrite 
=  HttpSessionStateRequirement.ReadWrite;
                            
if  (Utility.Settings.OldStyle.Contains( " sessionStateDefaultNone " ))
                            {
                                readWrite 
=  HttpSessionStateRequirement.None;
                            }
                            
if  (customAttributes.Length  >   0 )
                            {
                                useAsyncProcessing 
=  customAttributes[ 0 ].UseAsyncProcessing;
                                
if  (customAttributes[ 0 ].RequireSessionState  !=  HttpSessionStateRequirement.UseDefault)
                                {
                                    readWrite 
=  customAttributes[ 0 ].RequireSessionState;
                                }
                            }
                            
switch  (readWrite)
                            {
                                
case  HttpSessionStateRequirement.ReadWrite:
                                    
if  (useAsyncProcessing)
                                    {
                                        
return   new  AjaxAsyncHttpHandlerSession(processorArray[i]);
                                    }
                                    
return   new  AjaxSyncHttpHandlerSession(processorArray[i]);

                                
case  HttpSessionStateRequirement.Read:
                                    
if  (useAsyncProcessing)
                                    {
                                        
return   new  AjaxAsyncHttpHandlerSessionReadOnly(processorArray[i]);
                                    }
                                    
return   new  AjaxSyncHttpHandlerSessionReadOnly(processorArray[i]);

                                
case  HttpSessionStateRequirement.None:
                                    
if  (useAsyncProcessing)
                                    {
                                        
return   new  AjaxAsyncHttpHandler(processorArray[i]);
                                    }
                                    
return   new  AjaxSyncHttpHandler(processorArray[i]);
                            }
                            
if  ( ! useAsyncProcessing)
                            {
                                
return   new  AjaxSyncHttpHandlerSession(processorArray[i]);
                            }
                            
return   new  AjaxAsyncHttpHandlerSession(processorArray[i]);
                        }
                    }
                }
            }
            
else
            {
                
switch  (fileNameWithoutExtension.ToLower())
                {
                    
case   " prototype " :
                        
return   new  EmbeddedJavaScriptHandler( " prototype " );

                    
case   " core " :
                        
return   new  EmbeddedJavaScriptHandler( " core " );

                    
case   " ms " :
                        
return   new  EmbeddedJavaScriptHandler( " ms " );

                    
case   " prototype-core " :
                    
case   " core-prototype " :
                        
return   new  EmbeddedJavaScriptHandler( " prototype,core " );

                    
case   " converter " :
                        
return   new  ConverterJavaScriptHandler();

                    
default :
                        
if  (exception  !=   null )
                        {
                            
return   null ;
                        }
                        
if  ( ! ( ! Utility.Settings.OnlyAllowTypesInList  ||  flag))
                        {
                            
return   null ;
                        }
                        
return   new  TypeJavaScriptHandler(type);
                }
            }
        }
        
return   null ;
    }

  首先来获取处理页面的类型.再判断请求方式.如果是GET请求,则返回前文的四个引用文件.如果是POST请求,则是AJAX请求.然后判断是xmlHttpRequest对象发过来的请求,还是隐藏的IFrame框架发过来的请求.然后通过

(AjaxMethodAttribute[]) processorArray[i].AjaxMethod.GetCustomAttributes( typeof (AjaxMethodAttribute),  true );

来获取打了[AjaxMothod]标记的方法的标记方式,默认情况下,

public  AjaxMethodAttribute()
{
   
this .useAsyncProcessing  =   false ;
   
this .requireSessionState  =  HttpSessionStateRequirement.UseDefault;
}

最后跟据不同的属性组合来完成AJAX请求的处理,比如AjaxSyncHttpHandler

public   void  ProcessRequest(HttpContext context)
{
    
new  AjaxProcHelper( this .p).Run();
}

核心处理类为AjaxProcHelper

这个类代码很多,但最重要的只有两行:

静态AJAX处理方法:

 o  =   this .p.Type.InvokeMember( this .p.AjaxMethod.Name, BindingFlags.InvokeMethod  |  BindingFlags.Public  |  BindingFlags.Static,  null null , args);

实例AJAX处理方法:

=   this .p.AjaxMethod.Invoke(obj3, args);

可以看到,是通过反射直接调用的指定方法.这也可以解释为什么即使是实例的AJAX方法,也获取不到控件的状态.因为它根本就没有去执行页面的生命周期.

 

5.ajax请求完成后,如果没有回调函数,则直接结束.如果有回调函数,则执行doStateChange方法,然后是createResponse方法,最后在endRequest方法中完成回调函数的调用.

 

  以上就是一个完整的运用AjaxPro框架的AJAX请求过程.当然为了突出重点我省略了很多其它的要素.在查看这个框架的代码过程中,我感觉虽然我大致明白程序所表达的意思,但是有很多代码的编写方式让人难以理解.难道这就是传说中的混淆?

 

  参考的文章:

  ajaxpro 原理

  AjaxPro实现机制探讨——Ajax是如何调用服务器端C#方法的?

你可能感兴趣的:(ajax框架)