序
上一篇文章介绍了在webform平台上实现ajax的一些方法,并实现了一个基类。在本文中,我们将研究一个开源组件:ajaxpro。虽然这是一个老组件,但是实现思路和源代码都值得学习。通过上一篇文章的介绍,我们知道调用页面对象的方法是通过反射实现的,关键是整个处理过程,包括反射调用方法、参数映射等。ajaxpro不仅在后台为我们实现了这个过程,还在前台封装了请求调用的方法,比如Ajax的相关方法。通过ajaxpro的方法,异步请求可以在不封装js或使用js库的情况下发送。接下来,分析这个组件。
一. ajaxpro的使用。
让我们首先看看这个组件是如何使用的。
1.注册AjaxHandlerFactory。
在web.config中进行以下配置:
Httphandlers addverb=' post,get' path=' ajaxpro/*。ashx ' type=' Ajax pro . Ajax handler factory,ajaxpro'//httphandlers简而言之,请求的url符合ajaxpro/*的格式。阿什克斯。将由AjaxHandlerFactory处理,AJaxHandlerFactory是实现IHandlerFactory接口的工厂类,用于获取IHandler处理程序。类型的格式是:“名称控件”。类名,程序集名。
2.注册页面类Page_Load事件。
受保护的void Page_Load(对象发送者,事件参数e){ AjaxPro。utility . RegisterTypeForJax(type of(AJaxpropage));}我们将此Page对象的类型传递给了ResisterTypoForAjax方法,该方法用于在前台注册脚本。具体来说,它将调用当前页面对象的RegisterClientScriptBlock进行注册,因此在。否则脚本将不会被注册。(类型在这里传递,但实际上没有它也可以传递。这种类型也可以通过httpcontext在内部获得。current.handler.gettype()。basetype)。
3.用AjaxMethod标记该方法。
[AJaxmethod]public ListString GetList(string input 1,string input2){返回新的Liststring { input1,input 2 };}ajaxMethod是一个标签属性,表示这个方法是用来处理Ajax请求的,最后通过反射执行;它有几个构造函数对。对于一些需要缓存的数据,可以设置缓存时间。如果我们的请求不需要使用Session,我们可以设置HttpSessionStateRequirement;如果请求需要异步,例如请求耗时的web服务,处理程序也可以设置为异步状态。
方法的返回值可以是简单类型,也可以是复杂类型。例如,在前台获得的集合类型是一个数组。
4.前台电话。
后台的配置和使用非常简单。接下来,让我们看看前台是如何发起请求的。
函数GetList(){//var result=AJaxpronamespace。AjaxProPage.GetList('a ',' b ')。价值;//console.log(结果);AjaxProNamespace。AjaxProPage.GetList('a ',' b '),函数(结果){ console.log(结果);});}这里的AjaxProNamespace是页面类所在的命名空间,AjaxProPage是页面类的名称,GetList是标记的方法。为什么可以这样写?如前所述,ajaxpro会在前台注册脚本,它会根据我们页面对象的相关信息生成以下脚本,这样我们就可以这样调用它们了,不用自己写js,也不用使用jquery库。
if(类型为AJaxpronamespace==' undefined ')AJaxpronamespace={ };if(类型为AjaxProNamespace。AJaxpropage _ class==' undefined ')AJaxpronamespace。AJaxpropage _ class={ };AjaxProNamespace。AJaxpropage _ class=function(){ };Object.extend(AjaxProNamespace。AjaxProPage_class.prototype,Object.extend(新的AjaxPro。AjaxClass(),{ GetList:函数(input1,input 2){ return this . invoke(' GetList ',{'input1':input1,' input2':input2},this。GetList.getArguments()。切片(2));},URL : '/AJaxpro/AJaxpronameSpace。AjaxProPage,TestJaxprosourceCode . ashx ' });AjaxProNamespace。AjaxProPage=新的AjaxProNamespace。AJaxpropage _ class();GetList的参数对应后台方法的参数,类型必须是可转换的,否则调用会失败。最后一个参数是回调函数。回调函数的参数是封装返回结果的对象,其value属性是成功执行后返回的值。如上所述,它是一个数组对象。错误包括失败的信息。
注意,上面提到的部分是同步请求的方法,这通常不是我们想要的。我看到有人用错了。
第二,ajaxpro处理请求的原则。
本文主要关注组件处理ajax请求的过程,不介绍其他辅助功能。
1.生成辅助脚本。
在Page_Load事件中,我们调用了Ajax pro . utility . registertypeforjax(type of(Ajax pro Page));用于注册所需的脚本。我们注意到前台页面中引入了以下脚本:
也就是说,每个页面都会发起这些请求。这些文件都在。但是事实上它们都是js代码。有些js作为资源嵌套在dll内部,有些js是自动生成的,主要封装与ajax请求相关的方法,让我们通过:命名空间、页面类名、标记方法名来调用方法。为什么要用。ashx而不是。js?作为组件内部的资源文件,外部不能直接请求。js文件但是。可以截取ashx,然后用Response输出内容。写
如果每次生成和发送这些脚本的效率都很低,那么ajaxpro的内部处理就是判断请求头的If-None-Math和If-Modified-before,如果两者都和缓存一样,就会返回一个304状态码。因此,客户端只第一次请求服务器返回文件内容,所有后续请求只返回304,表示使用了本地缓存。我们可以通过刷新页面来验证此过程:
2.拦截请求。
HttpHandler(IHttpHandler)和HttpModule(ihttpmmodule)是ASP.NET的两个重要组成部分,让我们可以在ASP.NET的基础上轻松拓展。HttpHandler对应于特定的请求,例如。阿什克斯。aspx等。HttpModule是一个拦截器,它可以在管道中的一个事件处拦截所有请求。简单来说,在管道中,HttpApplication将触发一系列事件,我们正在通过HttpModule注册一个事件。例如,我们可以在生成处理程序对象之前拦截请求,然后将其映射到我们自己的处理程序。请求的实际处理结果是HttpHandler,例如,Page用于生成html。
以ASP.NET MVC框架为例,它基于ASP.NET路由机制。ASP.NET路由系统通过一个UrlRoutingModule拦截请求,具体来说就是拦截PostResolveRequestCache事件,解析URL,封装相应的路由数据,最后将请求传递给MvcHandler进行处理。MvcHandler实现了IHttpHandler接口。
以前进行过以下配置:addverb=' post,get' path=' ajaxpro/*。ashx' type=' ajaxpro。ajaxhandlerfactory,aja xpro '/这表示任何以ajaxpro/anyname结尾的Post/Get请求。阿什克斯。把它们交给AjaxPro。用于处理的AjaxHandlerFactory,它是实现IHandlerFactory的处理程序工厂,用于生成具体的IHttpHandler。组件内部定义了几个实现IHttpHandler的类,其中一些用于生成js脚本。对于处理ajax请求,主要分为两类:异步(IHttpAsyncHandler)和非异步(ihttphandler);在这两大类的基础上,对Session状态的支持有三种:支持读写的Handler(实现IRequiresSessionState标记接口)、只读的Handler(实现IReadOnlySessionState标记接口)、不支持Session的Handler。生成什么样的处理程序由AjaxMethod判断。
IHttpHandler的ProcessRequest(异步是BeginProcessRequest)用于执行请求并返回输出结果。如果只需要一个处理程序,我们也可以实现IHttpHandler。IHandlerFactory的定义如下:
公共接口IHttpHandlerFactory { IHttpHandler GetHandler(HttpContext context,string requestType,string url,string path translated);void release handler(IHttpHandler处理程序);}因此,ajaxpro的所有请求都将符合ajaxpro/*的格式。ashx,然后在GetHandler方法中,可以进行具体的处理,返回的结果是IHttpHandler;以非异步状态为例,如果我们配置Session,我们将生成一个实现IHttpHandler和IRequiresSessionState的Handler,如果我们需要只读Session,我们将生成一个实现IHttpHandler和IReadOnlySessionState的Handler。这些信息可以通过反射从AjaxMethod标记属性中获得。AjaxHandlerFactory的主要代码如下:
公共IHttpHandler GetHandler(HttpContext上下文,字符串requestType,字符串url,字符串路径已翻译){ 0字符串文件名=路径getfilename不带文本扩展(上下文。请求。路径);类型t=null异常类型异常=空;bool isInTypesList=false开关(RequestType){//获取请求,获取前面的那四个脚本大小写“GET”:开关(文件名. ToLower()){ case ' prototype ' :返回新的嵌入式JavaScript处理程序(“原型”);案例“核心”:返回新嵌入式JavaScript处理程序(' core ');案例' ms':返回新的嵌入式JavaScript处理程序(' ms ');案例“原型-核心”:案例“核心-原型”:返回新的嵌入式JavaScript处理程序('原型,核心');案例“转换器”:返回新的converter JavaScript处理程序();默认值:返回新的TypeJavaScript处理程序(t);} case ' POST ' : iajax处理器[]p=新iajax处理器[2];p[0]=new XmlHttpRequestProcessor(context,t);p[1]=新的IFrameProcessor(上下文,t);for(int I=0;仪表板长度;i ) { if (p[i]).CanHandleRequest) { //获取标记方法的AjaxMethod属性AJaxmethodattribute[]ma=(AJaxmethodattribute[])p[I]。AJaxmethod。GetcustomAttributes(类型为(AJaxmethodattribute),true);bool use sync=false httpassionstaterequisition会话请求=httpassionstaterequirement .读写;如果(马。长度0){使用sync=ma[0].使用同步处理;if (ma[0]).RequireSessionState!=HttpSessionStateRequirement .UseDefault) sessionReq=ma[0]。需要会话状态;} //6种汉德勒,根据是否异步,会话状态返回指定的处理程序开关(session req){ case HttpsessionStaterequirement .Read: if(!使用同步)返回新的Ajax synchtthandlerssionreadonly(p[I]);否则返回新的Ajax synchttphandlersissionreadonly(p[I]);案例HttpsessionStaterequirement .读写: if(!使用同步)返回新的ajaxsynchhandlersession(p[I]);否则返回新的Ajax synchttphandlersession(p[I]);案例HttpsessionStaterequirement .None: if(!使用同步)返回新的AJaxsynchHandler(p[I]);否则返回新的Ajax synchttphandler(p[I]);default: if(!使用同步)返回新的ajaxsynchhandlersession(p[I]);否则返回新的Ajax synchttphandlersession(p[I]);} } } break}返回null}3。反射执行方法
当获得一个处理本次请求的处理者后,就可以在其ProcessRequest(异步为BeginProcessRequest)执行指定的方法。要执行一个页面对象的方法,我们必须知道指定页面所在的程序集,名称空间,页面类的名称以及方法的名称。这似乎符合我们前面:名称空间。类名称。方法名称的调用方式。为了与一般请求区分开,让组件具有足够的独立性,ajaxpro只拦截符合ajaxpro/* .ashx格式的请求,这说明我们的创建交互式、快速动态网页应用的网页开发技术请求也要符合这个格式。如:http://localhost :50712/AJaxpro/AJaxpronamespace .AjaxProPage,TestAjaxProSourceCode.ashx,这个格式由前台脚本自动生成,并不需要我们去构造。仔细观察,会发现AjaxProNamespace .AjaxProPage,TestAjaxProSourceCode就是页面类的完全限定名:名称空间。类名称,程序集名称,通过这个我们就可以生成具体的类型,然后进行反射获取信息。那么方法的名称呢?ajaxpro将其放在超文本传送协议(Hyper Text Transport Protocol的缩写)头中,名称为:X-AJaxpro-方法。有了这些信息,就可以反射执行方法了。这里核心代码为:
内部void Run(){ try { //设置输出结果不缓存(这不一定是我们想要的p .背景。回应。expires=0;上下文响应缓存设置缓存能力(系统网络。http package ability。没有缓存);p .背景。回应。内容类型=内容类型;内容编码=系统文字。编码。UTF8//验证创建交互式、快速动态网页应用的网页开发技术请求if(!p . IsValidAjaxToken()){ p . SerializeObject(新系统。安全性。安全性例外(' AjaxPro令牌无效。'));返回;} //方法参数对象数组对象[]po=null;//请求处理结果对象res=null尝试{ //获取参数po=p . retrieveParameters();} catch(异常ex){} //获取缓存的密钥字符串cacheKey=p . type。全名“|”p . GetType().名称“|”p . AJaxmethod。名称“|”p . GetHashCode();if (p.Context.Cache[cacheKey]!=null) { //如果缓存存在,则直接使用缓存p .背景。回应。添加标题(' X ')常量. AjaxID '-Cache ',' server ');上下文。响应。写入(上下文。缓存[缓存键]);返回;}尝试{ if (p.AjaxMethod.IsStatic) { //使用反射调用静态方法尝试{ RES=p . type。invoke member(p . AJaxmethod。名称,系统.反射。绑定标签。静态|系统。反射。绑定标签。公共|系统。反射。绑定标签。InvokeMethod,null,null,po);} catch(Exception ex){ } } else { try {//创建实例对象,反射调用实例方法对象c=(对象)激活器.CreateInstance(p.Type,new object[]{ });if (c!=null){ RES=p . AJaxmethod。调用(c,po);} } catch(Exception ex){ } } } catch(Exception ex){ } try {//判断结果是不是xml,如是设置ContentType if (res!=空RES . GetType()==类型(系统.XML。XMl文档){ p . context。回应。内容类型='文本/XML ';内容编码=系统文字。编码。UTF8(系统Xml。XmlDocument)res ).保存(p .背景。回应。output stream);返回;}字符串结果=null系统。新系统文本。StringBuilder();尝试{ result=p . SerializeObject(RES);} catch(异常ex){} //如果需要缓存,则将结果写入缓存if(p . ServerCacheAttributes。长度0){ if(p . ServerCacheAttributes[0]).IsCacheEnabled){ p . context。缓存。添加(缓存键,结果,空,日期时间.现在,添加(服务器缓存属性[0]).缓存持续时间),系统网络。缓存。缓存。没有滑动到期,系统.网络。缓存。cacheitepriority。normal,null);} } } catch(Exception ex){ } } catch(Exception ex){ } }三、总结
我们总结一下ajaxpro的核心处理流程,它通过一个IHttpHandlerFactory拦截指定格式的url,然后从中获取类型的完全限定名生成类型对象,接着通过反射获取标记方法的特性,生成一个自定义的实现IHttpHandler接口的对象;在其ProcessRequest方法中,从超文本传送协议(Hyper Text Transport Protocol的缩写)头获取方法名称,通过反射进行参数映射并执行函数。
ajaxpro具有如下优点:
1.配置简单。
2.可以配合其它组件一起使用。
3.封装前台脚本,我们不用自己封装或者使用其它脚本库。
4.对返回值处理,我们可以返回简单类型或者复杂类型都会自动序列化。
缺点是:
1.页面会多出四个请求。尽管会利用304缓存,但还是需要发送请求到服务器。
2.创建交互式、快速动态网页应用的网页开发技术无法使用得到请求。由于自定义了全球资源定位器(统一资源定位符)格式,使用这种格式就无法用得到请求了,我们知道得到请求是可以被浏览器缓存的,雅虎前端优化建议中有一条就是多用得到请求。事实上,应该把名称空间。类名称,程序集放到超文本传送协议(Hyper Text Transport Protocol的缩写)头中,然后提供了一个类型类型的参数让我们自由选择。
3.与表单运行时间='服务器'绑定。目的是用了为我们生成前台脚本,但如果我们希望用。超文本标记语言文件。aspx.cs的方式就不能用了(博客园有些页面就用了这种方式);甚至我们的接口可能要给移动端使用,这种方便就变成了限制。
4.反思。这是低效的,它甚至不像我们以前的页面类那样缓存MethodInfo。
可以看出,如果效率不是太重要,这个组件是值得使用的。这只是一个核心介绍。它还有许多其他功能。这是ajaxpro组件的源代码。有兴趣的朋友可以研究一下。