<httpHandlers> <add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/></httpHandlers>简单的说,请求的url符合 ajaxpro/*.ashx 格式的,都会被AjaxHandlerFactory处理,这是一个实现IHandlerFactory接口的工厂类,用来获取IHandler处理程序。其中type的格式是:"名称控件.类名称,程序集名称"。
protected void Page_Load(object sender, EventArgs e){ AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage));}我们传递了本页面对象的Type给ResisterTypoForAjax方法,这个方法用来在前台注册脚本,具体会调用当前Page对象的RegisterClientScriptBlock进行注册,所以.aspx文件中必须有一个<form runat="server"></form>,否则脚本将无法注册。(这里传递了Type,实际也可以做到不用传递的,内部通过HttpContext.Current.Handler.GetType().BaseType 也可以获得这个类型)
[AjaxMethod]public List<string> GetList(string input1,string input2){ return new List<string> { input1, input2 };}AjaxMethod是一个标记属性,表示这个方法用于处理ajax请求,它最终通过反射执行;它有几个构造函数对,对于有些需要缓存的数据,可以设置缓存时间;如果我们的请求不需要使用Session,可以设置HttpSessionStateRequirement;如果请求需要异步,例如请求一个耗时的web服务,也可以设置处理程序为异步状态。
function GetList() { //var result = AjaxProNamespace.AjaxProPage.GetList("a", "b").value; //console.log(result); AjaxProNamespace.AjaxProPage.GetList("a", "b", function (result) {console.log(result); });}
if(typeof AjaxProNamespace == "undefined") AjaxProNamespace={};if(typeof AjaxProNamespace.AjaxProPage_class == "undefined") AjaxProNamespace.AjaxProPage_class={};AjaxProNamespace.AjaxProPage_class = function() {};Object.extend(AjaxProNamespace.AjaxProPage_class.prototype, Object.extend(new AjaxPro.AjaxClass(), { GetList: function(input1, input2) {return this.invoke("GetList", {"input1":input1, "input2":input2}, this.GetList.getArguments().slice(2)); }, url: "/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx"}));AjaxProNamespace.AjaxProPage = new AjaxProNamespace.AjaxProPage_class();GetList的参数对应后台方法的参数,类型必须可以转换,否则调用会失败。最后一个参数为回调函数,回调函数的参数是对返回结果进行封装的对象,其value属性就是执行成功返回的值,如上面返回的就是一个数组对象。其error包括了失败的信息。
也就是每个页面都会都会发起这几个请求。这几个都是.ashx结尾的文件,但实际里面都是js代码;这些js有的是作为资源嵌套在dll内部,有的是自动生成的,主要是封装了ajax请求相关方法,以及让我们可以用:名称空间.页面类名称.标记方法名称 这样去调用方法。为什么要用.ashx而不是用.js呢?因为作为组件内部的资源文件,外部无法直接请求.js文件,而.ashx可以被拦截,然后用Response.Write将内容输出。
如果每次都生成和发送这些脚本的效率是很低的,ajaxpro内部的处理是判断请求头的If-None-Math和If-Modified-Since,如果两个都和缓存的一样,就返回一个304状态码。所以,客户端只有首次请求服务端会返回文件的内容,后续的都只返回304表示使用本地缓存。我们刷新页面可以验证这个过程:
2. 拦截请求
HttpHandler(IHttpHandler) 和 HttpModule(IHttpModule) 是asp.net 两个重要的组件,让我们可以在asp.net的基础上很方便的进行扩展。HttpHandler对应某种具体的请求,例如.ashx,.aspx等;HttpModule是一个拦截器,可以在管道的某个事件对所有请求进行拦截。简单的说,在管道中,HttpApplication会触发一系列事件,我们在通过HttpModule对某个事件进行注册,例如我们可以在处理程序对象生成前拦截请求,然后映射到自己的处理程序;而实际处理请求返回结果的是HttpHandler,例如Page用来生成html。
以asp.net mvc框架为例,它是建立在asp.net 路由机制的基础上的,asp.net 路由系统通过一个UrlRoutingModule对请求进行拦截,具体是在PostResolveRequestCache事件进行拦截,对url进行解析,封装相应的路由数据后,最终将请求交给一个MvcHandler进行处理,MvcHandler实现了IHttpHandler接口。
前面我们进行了如下配置:<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/> 这表明了任何的以 ajaxpro/任意名称.ashx结尾的 Post/Get 请求,都交给AjaxPro.AjaxHandlerFactory进行处理,它是一个实现了IHandlerFactory的处理程序工厂,用来生成具体的IHttpHandler。组件内部定义了多个实现IHttpHandler的类,有的是为了生成js脚本的,对于处理ajax请求,主要分为两类:异步(IHttpAsyncHandler)和非异步(IHttpHandler);在这两类的基础上,对于Session的状态的支持又分为三种:支持读写(实现IRequiresSessionState标记接口)的Handler、只读(实现IReadOnlySessionState标记接口)的Handler和不支持Session的Handler。具体生成什么样的Handler是通过AjaxMethod进行判断的。
IHttpHandler的ProcessRequest(异步就是BeginProcessRequest)就用来执行请求返回输出结果的。如果只需要一种处理程序我们也可以实现IHttpHandler。IHandlerFactory的定义如下:
public interface IHttpHandlerFactory{ IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated); void ReleaseHandler(IHttpHandler handler);}
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated){ string filename = Path.GetFileNameWithoutExtension(context.Request.Path); Type t = null; Exception typeException = null; bool isInTypesList = false; switch (requestType) {//Get请求,获取前面的那4个脚本case "GET":switch (filename.ToLower()) {case "prototype": return new EmbeddedJavaScriptHandler("prototype");case "core": return new EmbeddedJavaScriptHandler("core");case "ms": return new EmbeddedJavaScriptHandler("ms");case "prototype-core":case "core-prototype": return new EmbeddedJavaScriptHandler("prototype,core");case "converter": return new ConverterJavaScriptHandler();default: return new TypeJavaScriptHandler(t); }case "POST": IAjaxProcessor[] p = new IAjaxProcessor[2]; p[0] = new XmlHttpRequestProcessor(context, t); p[1] = new IFrameProcessor(context, t); for (int i = 0; i < p.Length; i++) {if (p[i].CanHandleRequest){ //获取标记方法的AjaxMethod属性 AjaxMethodAttribute[] ma = (AjaxMethodAttribute[])p[i].AjaxMethod.GetCustomAttributes(typeof(AjaxMethodAttribute), true); bool useAsync = false; HttpSessionStateRequirement sessionReq = HttpSessionStateRequirement.ReadWrite; if (ma.Length > 0) {useAsync = ma[0].UseAsyncProcessing;if (ma[0].RequireSessionState != HttpSessionStateRequirement.UseDefault) sessionReq = ma[0].RequireSessionState; } //6种Handler,根据是否异步,session状态返回指定的Handler switch (sessionReq) {case HttpSessionStateRequirement.Read: if (!useAsync)return new AjaxSyncHttpHandlerSessionReadOnly(p[i]); elsereturn new AjaxAsyncHttpHandlerSessionReadOnly(p[i]);case HttpSessionStateRequirement.ReadWrite: if (!useAsync)return new AjaxSyncHttpHandlerSession(p[i]); elsereturn new AjaxAsyncHttpHandlerSession(p[i]);case HttpSessionStateRequirement.None: if (!useAsync)return new AjaxSyncHttpHandler(p[i]); elsereturn new AjaxAsyncHttpHandler(p[i]);default: if (!useAsync)return new AjaxSyncHttpHandlerSession(p[i]); elsereturn new AjaxAsyncHttpHandlerSession(p[i]); }} } break; } return null;}3. 反射执行方法
internal void Run(){ try {//设置输出结果不缓存(这不一定是我们想要的)p.Context.Response.Expires = 0;p.Context.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);p.Context.Response.ContentType = p.ContentType;p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;//验证ajax请求if (!p.IsValidAjaxToken()){ p.SerializeObject(new System.Security.SecurityException("The AjaxPro-Token is not valid.")); return;}//方法参数对象数组object[] po = null;//请求处理结果object res = null;try{ //获取参数 po = p.RetreiveParameters();}catch (Exception ex){}//获取缓存的Keystring cacheKey = p.Type.FullName + "|" + p.GetType().Name + "|" + p.AjaxMethod.Name + "|" + p.GetHashCode();if (p.Context.Cache[cacheKey] != null){ //如果缓存存在,则直接使用缓存 p.Context.Response.AddHeader("X-" + Constant.AjaxID + "-Cache", "server"); p.Context.Response.Write(p.Context.Cache[cacheKey]); return;}try{ if (p.AjaxMethod.IsStatic) {//使用反射调用静态方法try{ res = p.Type.InvokeMember(p.AjaxMethod.Name,System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.InvokeMethod,null, null, po);}catch (Exception ex){} } else {try{ //创建实例对象,反射调用实例方法 object c = (object)Activator.CreateInstance(p.Type, new object[] { }); if (c != null) {res = p.AjaxMethod.Invoke(c, po); }}catch (Exception ex){} }}catch (Exception ex){}try{ //判断结果是不是xml,如是设置ContentType if (res != null && res.GetType() == typeof(System.Xml.XmlDocument)) {p.Context.Response.ContentType = "text/xml";p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;((System.Xml.XmlDocument)res).Save(p.Context.Response.OutputStream);return; } string result = null; ; System.Text.StringBuilder sb = new System.Text.StringBuilder(); try {result = p.SerializeObject(res); } catch (Exception ex){} //如果需要缓存,则将结果写入缓存 if (p.ServerCacheAttributes.Length > 0) {if (p.ServerCacheAttributes[0].IsCacheEnabled){ p.Context.Cache.Add(cacheKey, result, null, DateTime.Now.Add(p.ServerCacheAttributes[0].CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);} }}catch (Exception ex){} } catch (Exception ex){}}三、总结