From www.magicajax.net
和其他流行的ajax框架,比如ajax.net等相比,magicajax的设计思路很有特点,同样是利用ajax技术,他的方便和易用十分地引人注目。
从开发人员使用角度讲,在ajax.net框架下,我们需要注册一个公布给客户端的类,然后在客户端脚本里访问,然后再根据从服务端返回的数据自己控制客户端界面的改变,使用起来不是十分方便。而在magicajax的框架下,我们甚至不需要写一行代码就实现了无刷新的网页,在浏览器里工作就像在winform里一样,给用户以无缝体验,大大地简化了开发人员的工作。
从设计架构的角度讲,ajax.net框架参考了prototype.js这个纯脚本ajax框架,如果你注册了一个类型到ajax.net里,那么在输出到客户端的脚本里就会有ajax.net为你生成的javascript对象,开发人员就像调用服务端对象一样使用,其实背后都是用脚本模拟的,说起来倒有点像remoting的对象截获。当然,如果需要显示什么东西的话,比如填充某个下拉框就得自己动手,操作dhtml。而magicajax则是另外一种思路,我们不是要页面无刷新吗?OK,只要把你要执行服务端事件的控件放到ajapanel里,magicajax就在后台将页面要提交的数据提交回去,交给IIS,再传递给注册在配置文件中的ajaxmodule,他会负责返回被其框架解析的脚本语句,来反映出服务器操作造成的变化,客户端eval一下就可以了。而如何得到实现这种变化的脚本语句就成为代码主要完成的工作。
从创新的角度讲,通过使用储存页面的配置,magicAjax几乎改变了web的编程模型,真正意义上实现了桌面程序模型的web编程。在这后面分析源码的过程中再详细介绍。
前面的叙述只是个轮廓,现在开始一步步地解析magicajax的源码。Magicajax前不久发布了
如果要使用magixajax,除了引用dll以外,我们还需在配置文件里加上:
/// <add name="MagicAjaxModule" type="MagicAjax.MagicAjaxModule, MagicAjax" />
/// </httpModules>
我们可以从作用的入口点:MagicAjaxModule 开始分析。
MagicAjaxModule实现了IHttpModule接口,实现IHtppModule的Init方法,绑定应用程序HttpApplication的BeginRequest, AcquireRequestState, EndRequest事件。
在BeginRequest阶段,MA储存了当前上下文的Request和Response变量,这是后面分析请求以及输出脚本所必需的对象。这阶段还对AjaxCallObject.js.aspx的请求做了响应,输出AjaxCallObject.js脚本。
最主要的处理则在AcquireRequestState阶段。考虑到页面的储存与否会导致两种截然不同的编程模型,为了描述方便,我们对两种pagestore方式分别展开讨论。
首先,对这部分代码的分析我们按<pageStore mode="NoStore">的配置进行分析。在这个部分,如果请求为get或非aspx页面则不进行处理,如果pagesstore=nostore,则让asp.net的框架根据提交的页面生成新的页面:string vsValue = _filter.GetViewStateFieldValue();
if (vsValue != null && _request.Form["__VIEWSTATE"] != vsValue)
{
AjaxCallHelper.WriteSetFieldScript("__VIEWSTATE", vsValue);
}
AjaxCallHelper.End();
{
if (_writingLevel > 0)
throw new MagicAjaxException("Script writing level should be 0 at the end of AjaxCall. IncreaseWritingLevel calls do not match DecreaseWritingLevel calls.");
WriteEndSignature();
HttpResponse hr = HttpContext.Current.Response;
hr.Clear();
MergeNextWritingLevelRecursive (0);
hr.Write ((_sbWritingLevels[0] as StringBuilder).ToString());
MagicAjaxContext.Current.CompletedAjaxCall = true;
hr.Flush();
hr.End();
}
这里就有一个问题,这些js语句是什么时候生成的呢?答案是Render()。
MA的容器ajaxpanel派生于抽象基类RenderedByScriptControl,RenderedByScriptControl重载了Render方法 ,Render方法了里调用WriteScript()写出脚本,WriteScript()又调用了抽象方法RenderByScript(),RenderByScript方法则实际上由ajaxpanel来完成的,呵呵,典型的模版模式。由于控件是放在ajaxpanel里的,所以在输出子控件内容的阶段,ajaxpanel就把子控件的html标记放在一段<span>标记里,一旦子控件输出的内容有变化就只需要改变对应的span标记的innerHtml就可以了,而不用去关心控件输出的哪些html内容出现了变化。
我们可以把注意力可以完全集中在ajaxpanel的RenderByScript方法上了,我们来看看子控件内容的变化是如何被识别和记录的:if (htmlFingerprint != (string)_controlHtmlFingerprints[con])
{
ExtendedWriteSetHtmlOfElementScript(html, GetAjaxElemID(con));
_controlHtmlFingerprints[con] = htmlFingerprint;
}
从上面的代码和分析中我们可以得出看出,nostore的ajax很好地维护了已有的asp.net编程模型,并没有像ajax.net一样破坏程序模型。而store模式的magicAjax则将webform的程序模型向winform史无前例地拉近了,也许,变革就在前方。
在store模型下,我们可以选择将页面实例对象储存在session和cache里,一旦有ajax请求,就从session或cache里获取对象,而不用再一次地去执行页面的一个生存周期,将临时页面对象换成了常驻内存的对象。
我们来看看这种模式下是如何获得体现改变的脚本代码的,以session储存方式为例。
在AcquireRequestState阶段,就不用像nostore那样调用HttpContext.Current.Handler.ProcessRequest来处理请求生成页面了,而是根据传回的pagekey从session中获取储存页面对象:string argument = _request.Form["__EVENTARGUMENT"];
RaisePostBackEventInChild (page, target, argument);
{
Control eventcontrol = page.FindControl(eventTarget);
if (eventcontrol is IPostBackEventHandler)
((IPostBackEventHandler)eventcontrol).RaisePostBackEvent(eventArgument);
}
框架的核心流程就是上面这些,为了使叙述清晰,我省略掉了一些东西,如果有兴趣可以自己看看代码,magicajax的注释写得很好,比较好理解。
那么,为什么说store模式的编程模型与winform接近呢?我这里借用一下叶子的这篇文章http://wj.cnblogs.com/archive/2005/12/29/307704.html中的一个示例:{
HyperLink link = new HyperLink();
link.Text = "HyperLink" + AjaxPanel1.Controls.Count;
AjaxPanel1.Controls.Add (link);
}
将桌面程序和web程序的编程模型统一起来几乎是很多开发人员的梦想,而现在,梦想突然变清晰了。这样好的东西有没有什么问题呢?当然有,从magicajax自身的角度讲,使用session储存很消耗服务器的资源,而且他的储存方式只支持InProc,如果能支持其他方式的储存相信会使其能真正走向成熟的应用,如果能将页面储存在客户端也许就将引爆web2.0的RIA革命。另外,让web开发人员回归winfrom的开发模型绝对是个挑战,遵循哪种编程模型肯定会让开发人员困惑不已,而现在并不一定完善成熟的版本可能导致的问题也让magicajax继续停留在实验室阶段。
我相信在可预见的将来,magixAjax及其思想将会影响未来的web开发,带来全新的开发模型。昙花一现还是革命的前奏,让我们拭目以待!在此也感谢叶子wj.cnblogs.com给我的帮助,使我对magicajax有了更多的认识!