Bien qu'ajaxpro soit un composant plus ancien, les idées d'implémentation et le code source ont toujours une grande valeur de référence. Ensuite, à travers cet article, je vais vous présenter comment écrire des composants ajax légers 02 - une brève analyse d'AjaxPro. Les amis intéressés peuvent se référer à l'
Avant-propos
L'article précédent a présenté le. implémentation sur la plateforme de formulaire Web Quelques façons d'ajax et d'implémenter une classe de base. Dans cet article, nous examinons un composant open source : ajaxpro. Bien qu'il s'agisse d'un composant plus ancien, les idées d'implémentation et le code source valent toujours la peine d'être appris. Grâce à l'introduction de l'article précédent, nous savons que l'appel de la méthode de l'objet page est réalisé par réflexion. La clé est l'ensemble du processus de traitement, y compris la méthode d'appel de réflexion, le mappage des paramètres, etc. Ajaxpro nous aide non seulement à implémenter ce processus en arrière-plan, mais encapsule également les méthodes d'appel de requête au premier plan, telles que les méthodes liées à ajax. Vous pouvez utiliser les méthodes ajaxpro pour envoyer des requêtes asynchrones sans avoir besoin d'encapsuler js vous-même ou d'utiliser un js. bibliothèque. Ensuite, nous analyserons brièvement ce composant.
1. Utilisation d'ajaxpro
Voyons d'abord comment utiliser ce composant.
1. Enregistrez AjaxHandlerFactory
Faites la configuration suivante dans web.config :
<httpHandlers> <add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/> </httpHandlers>
En termes simples, l'url demandée est conforme à ajaxpro/ Le format *.ashx sera traité par AjaxHandlerFactory, qui est une classe d'usine qui implémente l'interface IHandlerFactory et est utilisée pour obtenir le gestionnaire IHandler. Le format du type est : "Contrôle de nom. Nom de la classe, nom de l'assembly".
2. Inscrivez-vous dans l'événement Page_Load de la classe page
protected void Page_Load(object sender, EventArgs e) { AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage)); }
Nous passons le Type de cet objet page à la méthode ResisterTypoForAjax, qui est utilisée pour s'inscrire. le script au premier plan, en particulier le RegisterClientScriptBlock de l'objet Page actuel, sera appelé pour l'enregistrement, il doit donc y avoir un
dans le fichier .aspx, sinon le script sera ne pas être enregistré. (Le type est transmis ici, mais cela peut en fait être fait sans le transmettre. Ce type peut également être obtenu en interne via HttpContext.Current.Handler.GetType().BaseType)3. avec AjaxMethod
[AjaxMethod] public List<string> GetList(string input1,string input2) { return new List<string> { input1, input2 }; }
AjaxMethod est un attribut de balise, indiquant que cette méthode est utilisée pour gérer les requêtes ajax. Elle est finalement exécutée par réflexion et comporte plusieurs paires de constructeurs. être mis en cache, le cache peut être défini sur Time ; si notre requête n'a pas besoin d'utiliser Session, nous pouvons définir HttpSessionStateRequirement ; si la requête doit être asynchrone, comme la demande d'un service Web qui prend du temps, nous pouvons également définir le gestionnaire sur état asynchrone.
La valeur de retour d'une méthode peut être un type simple ou un type complexe par exemple, le type de collection obtenu au premier plan est un tableau ;
4. Appel front-end
La configuration et l'utilisation du back-end sont très simples. Voyons ensuite comment le front-end initie une requête.
function GetList() { //var result = AjaxProNamespace.AjaxProPage.GetList("a", "b").value; //console.log(result); AjaxProNamespace.AjaxProPage.GetList("a", "b", function (result) { console.log(result); }); }
Ici, AjaxProNamespace est l'espace de noms où se trouve la classe de page, AjaxProPage est le nom de la classe de page et GetList est la méthode marquée. Pourquoi peut-on écrire ainsi ? Comme mentionné précédemment, ajaxpro enregistrera le script au premier plan et générera le script suivant en fonction des informations pertinentes de notre objet page, nous pouvons donc l'appeler ainsi sans avoir à écrire nous-mêmes js ou à utiliser la méthode de la bibliothèque jquery.
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();
Les paramètres de GetList correspondent aux paramètres de la méthode d'arrière-plan. Le type doit être convertible, sinon l'appel échouera. Le dernier paramètre est la fonction de rappel. Le paramètre de la fonction de rappel est un objet qui encapsule le résultat renvoyé. Son attribut value est la valeur renvoyée après une exécution réussie. Par exemple, la valeur renvoyée ci-dessus est un objet tableau. Son erreur inclut des informations d'échec.
Notez que la partie commentée ci-dessus est une requête synchrone, ce qui n'est souvent pas ce que nous souhaitons. J'ai vu quelqu'un l'utiliser de manière incorrecte.
2. Principe du traitement des requêtes ajaxpro
Ici, nous nous concentrons principalement sur le processus de traitement des requêtes ajax par les composants, et les autres fonctions auxiliaires ne le seront pas. être introduit.
1. Générer un script auxiliaire
Dans l'événement Page_Load, nous avons appelé AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage)); Nous avons remarqué que le script suivant a été introduit sur la première page :
C'est-à-dire que chaque page lancera ces requêtes. Ce sont tous des fichiers se terminant par .ashx, mais ils sont en fait tous du code js ; certains de ces js sont imbriqués dans des dll en tant que ressources, et certains sont générés automatiquement. Ils encapsulent principalement les méthodes liées aux requêtes ajax et nous permettent d'utiliser : l'espace de noms. Le nom de la classe de page. Le nom de la méthode Tag est utilisé pour appeler la méthode. Pourquoi utiliser .ashx au lieu de .js ? Parce qu'en tant que fichier de ressources à l'intérieur du composant, le fichier .js ne peut pas être demandé directement de l'extérieur, mais le .ashx peut être intercepté, puis le contenu est généré à l'aide de 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接口。
前面我们进行了如下配置:
IHttpHandler的ProcessRequest(异步就是BeginProcessRequest)就用来执行请求返回输出结果的。如果只需要一种处理程序我们也可以实现IHttpHandler。IHandlerFactory的定义如下:
public interface IHttpHandlerFactory { IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated); void ReleaseHandler(IHttpHandler handler); }
所以,ajaxpro的所有请求都会符合ajaxpro/*.ashx格式,然后在GetHandler方法,就可以进行具体的处理,返回结果是IHttpHandler;以非异步状态为例,如果我们配置了需要Session,就会生成一个实现IHttpHandler和IRequiresSessionState的Handler,如果需要只读的Session,就会生成一个实现IHttpHandler和IReadOnlySessionState的Handler;这些信息可以通过反射从AjaxMethod标记属性获得。AjaxHandlerFactory的主要代码如下:
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]); else return new AjaxAsyncHttpHandlerSessionReadOnly(p[i]); case HttpSessionStateRequirement.ReadWrite: if (!useAsync) return new AjaxSyncHttpHandlerSession(p[i]); else return new AjaxAsyncHttpHandlerSession(p[i]); case HttpSessionStateRequirement.None: if (!useAsync) return new AjaxSyncHttpHandler(p[i]); else return new AjaxAsyncHttpHandler(p[i]); default: if (!useAsync) return new AjaxSyncHttpHandlerSession(p[i]); else return new AjaxAsyncHttpHandlerSession(p[i]); } } } break; } return null; }
3. 反射执行方法
当获得一个处理本次请求的Handler后,就可以在其ProcessRequest(异步为BeginProcessRequest)执行指定的方法。要执行一个页面对象的方法,我们必须知道指定页面所在的程序集,名称空间,页面类的名称以及方法的名称。这似乎符合我们前面:名称空间.类名称.方法名称的调用方式。为了与一般请求区分开,让组件具有足够的独立性,ajaxpro只拦截符合"ajaxpro/*.ashx格式的请求,这说明我们的ajax请求也要符合这个格式。如:http://localhost:50712/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx,这个格式由前台脚本自动生成,并不需要我们去构造。仔细观察,会发现AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode 就是页面类的完全限定名:名称空间.类名称,程序集名称,通过这个我们就可以生成具体的Type,然后进行反射获取信息。那么方法的名称呢?ajaxpro将其放在http header 中,名称为:X-AjaxPro-Method。有了这些信息,就可以反射执行方法了。这里核心代码为:
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){} //获取缓存的Key string 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){} }
三、总结
我们总结一下ajaxpro的核心处理流程,它通过一个IHttpHandlerFactory拦截指定格式的url,然后从中获取类型的完全限定名生成类型对象,接着通过反射获取标记方法的特性,生成一个自定义的实现IHttpHandler接口的对象;在其ProcessRequest方法中,从http headers获取方法名称,通过反射进行参数映射并执行函数。
ajaxpro présente les avantages suivants :
1. Configuration simple.
2. Peut être utilisé avec d'autres composants.
3. Encapsulez le script front-end Nous n'avons pas besoin de l'encapsuler nous-mêmes ou d'utiliser d'autres bibliothèques de scripts.
4. Pour le traitement des valeurs de retour, nous pouvons renvoyer des types simples ou des types complexes, qui seront automatiquement sérialisés.
Les inconvénients sont :
1. La page aura 4 requêtes supplémentaires. Bien que la mise en cache 304 soit utilisée, une requête doit toujours être envoyée au serveur.
2. Ajax ne peut pas utiliser la requête Get. Étant donné que le format de l'URL est personnalisé, les requêtes Get ne peuvent pas être utilisées avec ce format. Nous savons que les requêtes Get peuvent être mises en cache par les navigateurs. L'une des suggestions d'optimisation frontale de Yahoo consiste à utiliser davantage de requêtes Get. En fait, nous devrions mettre l'espace de noms, le nom de la classe et l'assembly dans l'en-tête http, puis fournir un paramètre de type que nous pouvons choisir librement.
3. Liez avec