现在仿佛比较流行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
具体内容如下:
< 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事件中,有一段代码:
主要作用是向客户端输出一段JS:
< script type = " text/javascript " src = " /ajaxTest/ajax/ajaxTest.WebForm1,App_Web_6jqhtozc.ashx " ></ script >
客户端执行这段代码后,就开始向服务器请求ajax/common.ashx这个文件了,但不会被请求到,因为在被PageHandlerFactory拦截后,服务器就已经根据情况(主要是看context.Request[ "_session" ]的值)返回不同的IHttpHandler。
第一次请求时返回的是一个AjaxHandlerSessionStateR,它的构造函数直接使用的是它的基类AjaxHandler。AjaxHandler重写了ProcessRequest方法,于是此方法执行:
proc.Run();
在第一次请求中,proc.Run();是关键,它向客户端输入了很多javascript代码,包括产生异步调用的代码和客户端的“DataSet”等。
异步调用的代码,是放在Ajax.JS文件里的,在proc.Run();的时候用AjaxProcessor的RenderCommonScript:
/// 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 AjaxHandler的ProcessRequest:
AjaxRequestProcessor proc = new AjaxRequestProcessor( context );
proc.Run();
}
这儿我们应该可以猜到所有的事都交给Run来完成了。好,我们深入进去看看。
Run的代码里有这么一句:
Line 49:
也许有人奇怪这是为什么,还加个时间,其实这个时间在后边作缓存时是有用的。
Line 57:
ajaxer.InitializeContext( ref context );
实例化一个AjaxProcessor,后边有用。
Line 64:
此时为false,所以不用再执行方法ajaxer.RenderCommonScript();(line 72)了。
Line 87:
MethodInfo[] mi = null ;
string methodName = null ;
object [] po = new object [] {} ;
string md5 = null ;
StreamReader sr = null ;
准备反射和内存流读取。
下图为type的实例化:
Line 99:
设置读取的buffer。
Line 100:
此时返回为true,以下代码被执行。
Line 101:
返回的md5是一串排列好了的hash code,MD5Helper的这个方法封装了MD5CryptoServiceProvider的ComputeHash方法,在后边会把这个md5和刚才我们得到的时间一起在缓存时使用。
Line 102:
开始流的读取。
Line 104:
if ( context.Cache[ type.FullName + " | " + md5 ] != null ) {
context.Response.Write( context.Cache[ type.FullName + "|" + md5 ].ToString() );
}
如果允许缓存,则直接使用之,它好我也好。
Line 119:
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;
}
开始反射。
如图:
记的我们在使用AJAX。NET的时候要在公有方法前加一个
[BorgWorX.Web.Core.Ajax.AjaxMethod()]这样的属性吗?现在有用了。不过ii.Length != 1将会为false,所以不会return,代码继续往下走。
Line 139:
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:
o是用来放结果的。
Line 191:
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 ,方便编程。