现在仿佛比较流行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,它的构造函数直接使用的是它的基类AjaxHandler。AjaxHandler重写了ProcessRequest方法,于是此方法执行:
AjaxRequestProcessor proc = new AjaxRequestProcessor( context );
proc.Run();
在第一次请求中,proc.Run();是关键,它向客户端输入了很多javascript代码,包括产生异步调用的代码和客户端的“DataSet”等。
异步调用的代码,是放在Ajax.JS文件里的,在proc.Run();的时候用AjaxProcessor的RenderCommonScript:
/**//// <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
AjaxHandler的ProcessRequest:
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
code,MD5Helper的这个方法封装了MD5CryptoServiceProvider的ComputeHash方法,在后边会把这个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:
开始反射。
如图:
记的我们在使用AJAX。NET的时候要在公有方法前加一个
[BorgWorX.Web.Core.Ajax.AjaxMethod()]这样的属性吗?现在有用了。不过ii.Length
!= 1将会为false,所以不会return,代码继续往下走。
Line 139:
继续反射,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,方便编程。
|