求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
 
Ajax.NET源码分析
 

2010-06-18 作者:极地银狐.NET 来源:极地银狐.NET的blog

 

现在仿佛比较流行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 ) {
o = 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,方便编程。



使用decj简化Web前端开发
Web开发框架形成之旅
更有效率的使用Visual Studio
MVP+WCF+三层结构搭建框架
ASP.NET运行机制浅析【图解】
编写更好的C#代码
10个Visual Studio开发调试技巧
更多...   


.NET框架与分布式应用架构设计
.NET & WPF & WCF应用开发
UML&.Net架构设计
COM组件开发
.Net应用开发
InstallShield


日照港 .NET Framework & WCF应用开发
神华信息 .NET单元测试
北京 .Net应用软件系统架构
台达电子 .NET程序设计与开发
赛门铁克 C#与.NET架构设计
广东核电 .Net应用系统架构
更多...