Maison > interface Web > js tutoriel > Écriture de composants ajax légers 01-Comparaison avec diverses méthodes d'implémentation sur la plateforme de formulaire Web

Écriture de composants ajax légers 01-Comparaison avec diverses méthodes d'implémentation sur la plateforme de formulaire Web

亚连
Libérer: 2018-05-24 14:57:19
original
1402 Les gens l'ont consulté

Cet article présente principalement l'écriture du composant ajax léger 01 - par rapport à diverses méthodes d'implémentation sur la plateforme de formulaire Web. Les amis dans le besoin peuvent s'y référer

Avant-propos

Asp. .net WebForm et Asp.net MVC (MVC en abrégé) sont tous deux des frameworks de développement Web basés sur Asp.net. L'une d'elles est que MVC accorde plus d'attention à l'essence de http, tandis que WebForm essaie. pour protéger http. Cela fournit un grand nombre de contrôles serveur et de mécanismes ViewState, permettant aux développeurs de programmer sur la base du modèle d'événement, comme le développement d'applications Windows Form. Les deux ont leurs propres avantages, inconvénients et scénarios applicables, mais MVC est désormais le premier choix pour de nombreux développeurs Asp.net.

WebForm est basé sur Asp.net. Asp.net offre une évolutivité suffisante. Nous pouvons également les utiliser pour écrire des frameworks de type MVC sous WebForm. En ce qui concerne WebForm, beaucoup de gens penseront aux contrôles serveur (contrôles glisser !!!). En fait, ce n'est pas le cas. Nous pouvons également ne pas utiliser du tout les contrôles serveur et nous concentrer sur le HTML comme MVC. Si WebForm souhaite abandonner les contrôles du serveur et se concentrer sur le HTML, il doit d'abord supprimer la balise

. Ce formulaire de serveur runat est la base de son mécanisme PostBack. Puisque nous revenons à html+css+js, cela signifie que beaucoup de choses doivent être implémentées par nous-mêmes, comme la gestion des requêtes Ajax. Contrairement à MVC, la conception initiale de WebForm utilise les contrôles serveur comme composant principal. Si vous ne l'utilisez pas, vous ne pouvez utiliser que son extensibilité pour y parvenir.

 Cette série vise à implémenter un composant ajax léger basé sur la plateforme WebForm, qui est principalement divisée en trois parties :

 1. Présentation de diverses méthodes d'implémentation sous WebForm.

 2. Analysez les composants ajaxpro.

 3. Écrivez votre propre composant ajax.

1. Introduction à Ajax

Asynchrone nous permet de demander ou de soumettre des données au serveur sans actualiser la page entière. Pour les pages complexes, il est évidemment inefficace de recharger la page entière juste pour demander un peu de données. Ajax est conçu pour résoudre ce problème. Le cœur d'ajax est l'objet XmlHttpRequest, via lequel les requêtes sont soumises au serveur sous forme de texte. Après XmlHttpRequest2.0, la soumission de données binaires est également prise en charge.

Sécurité Ajax : pour des raisons de sécurité, Ajax est limité par la politique de même origine ; c'est-à-dire que seules les demandes pour le même domaine et le même port sont accessibles, et les demandes inter-domaines seront rejetées. Bien entendu, les exigences nécessitent parfois l'envoi de requêtes entre domaines. Les méthodes de traitement inter-domaines couramment utilisées incluent CORS (partage de ressources entre domaines) et JSONP (JSON paramétrique).

Format d'interaction de données Ajax : Bien que l'objet principal Ajax XmlHttpRequest comporte le mot "XML", le format d'échange de données entre le client et le serveur ne se limite pas au XML. Par exemple, le format json est désormais plus souvent utilisé. . ​

L'Ajax a aussi des défauts. Par exemple, la prise en charge des moteurs de recherche n'est pas très bonne ; elle viole parfois l'intention initiale du positionnement des ressources URL.

2. Utilisation d'ajax sous la plateforme Asp.net MVC

Dans MVC, il est très pratique pour ajax d'appeler des méthodes en arrière-plan. il suffit de spécifier l’action. Nommez-la simplement.

Code front-end :

<body>
  <h1>index</h1>
  <input type="button" value="GetData" onclick="getData()" />
  <span id="result"></span>
</body>
<script type="text/javascript">
  function getData() {
    $.get("GetData", function (data) {
      $("#result").text(data);
    });
  }
</script>
Copier après la connexion

Code back-end :

public class AjaxController : Controller
{
  public ActionResult GetData()
  {
    if(Request.IsAjaxRequest())
    {
      return Content("data");
    }
    return View();
  }
}
Copier après la connexion

3. Utilisation d'ajax sous la plateforme WebForm

 3.1 Basé sur un package de contrôle de serveur ou des composants tiers

Ceci est basé sur des contrôles de serveur, tels que la boîte à outils ajax, ou des composants comme FineUI. Le front-end Web est toujours composé de html+css+js, mais la question est de savoir comment le générer. Nous pouvons écrire les plug-ins natifs nous-mêmes ou utiliser des plug-ins front-end ; ceux basés sur les contrôles du serveur sont générés en arrière-plan et sont généralement moins efficaces. Le composant serveur générera une série de proxys au premier plan. L'essence est toujours la même, mais le contrôle encapsule ce processus et ne nous oblige pas à l'écrire nous-mêmes. Le modèle basé sur des contrôles ou des composants tiers est très utile dans certains systèmes de gestion. Le nombre de visites n'est pas très important et il peut être développé rapidement.

 3.2 Basé sur l'interface ICallbackEventHandler

 .net fournit l'interface ICallbackEventHandler pour traiter les demandes de rappel. Cette interface doit utiliser ClientScriptManager pour générer des scripts proxy au premier plan pour l'envoi et la réception de requêtes, la balise

Code front-end :

<body>
  <form id="form1" runat="server">
  <p>    
    <input type="button" value="获取回调结果" onclick="callServer()" />
    <span id="result" style="color:Red;"></span>
  </p>
  </form>
</body>
<script type="text/javascript">
  function getCallbackResult(result){
    document.getElementById("result").innerHTML = result;
  }
</script>
Copier après la connexion

Code back-end :

public partial class Test1 : System.Web.UI.Page, ICallbackEventHandler
{    
  protected void Page_Load(object sender, EventArgs e)
  {
    //客户端脚本Manager
    ClientScriptManager scriptMgr = this.ClientScript;
 
    //获取回调函数,getCallbackResult就是回调函数
    string functionName = scriptMgr.GetCallbackEventReference(this, "", "getCallbackResult", "");
 
    //发起请求的脚本,callServer就是点击按钮事件的执行函数
    string scriptExecutor = "function callServer(){" + functionName + ";}";
 
    //注册脚本
    scriptMgr.RegisterClientScriptBlock(this.GetType(), "callServer", scriptExecutor, true);
  }
 
  //接口方法
  public string GetCallbackResult()
  {
    return "callback result";
  }
 
  //接口方法
  public void RaiseCallbackEvent(string eventArgument)
  {
  }
}
Copier après la connexion

 Cette méthode présente les inconvénients suivants :

 1. Elle est plus compliquée à mettre en œuvre et un script correspondant doit être enregistré pour chaque événement de chargement de page.

 2. Le frontend générera un fichier de script pour le proxy.

 3. Pour les interactions de pages complexes, c'est très difficile à mettre en œuvre.

 4. Bien qu'il s'agisse d'un rappel, l'objet page est toujours généré à ce moment.

 3.3 Utilisation des gestionnaires généraux

  一般处理程序其实是一个实现了IHttpHandler接口类,与页面类一样,它也可以用于处理请求。一般处理程序通常不用于生成html,也没有复杂的事件机制,只有一个ProcessRequest入口用于处理请求。我们可以将ajax请求地址写成.ashx文件的路径,这样就可以处理了,而且效率比较高。

  要输出文本内容只需要Response.Write(data)即可,例如,从数据库获取数据后,序列化为json格式字符串,然后输出。前面说到,一般处理程序不像页面一样原来生成html,如果要生成html,可以通过加载用户控件生成。如:

public void ProcessRequest(HttpContext context)
{
  Page page = new Page();
  Control control = page.LoadControl("~/PageOrAshx/UserInfo.ascx");
  if (control != null)
  {
    StringWriter sw = new StringWriter();
    HtmlTextWriter writer = new HtmlTextWriter(sw);
    control.RenderControl(writer);
    string html = sw.ToString();
    context.Response.Write(html);        
  }
}
Copier après la connexion

  这种方式的优点是轻量、高效;缺点是对于交互多的需要定义许多ashx文件,加大了管理和维护成本。

  3.4 页面基类

  将处理ajax请求的方法定义在页面对象内,这样每个页面就可以专注处理本页面相关的请求了。这里有点需要注意。

  1.如何知道这个请求是ajax请求?

    通过请求X-Requested-With:XMLHttlRequest 可以判断,大部份浏览器的异步请求都会包含这个请求头;也可以通过自定义请求头实现,例如:AjaxFlag:XHR。

  2.在哪里统一处理?

    如果在每个页面类里判断和调用是很麻烦的,所以将这个处理过程转到一个页面基类里处理。

  3.如何知道调用的是哪个方法?

    通过传参或者定义在请求头都可以,例如:MethodName:GetData。

  4.知道方法名称了,如何动态调用?

    反射。

  5.如何知道该方法可以被外部调用?

    可以认为public类型的就可以被外部调用,也可以通过标记属性标记。

  通过上面的分析,简单实现如下  

  页面基类:

public class PageBase : Page
{
  public override void ProcessRequest(HttpContext context)
  {
    HttpRequest request = context.Request;
    if (string.Compare(request.Headers["AjaxFlag"],"AjaxFlag",0) == 0)
    {
      string methodName = request.Headers["MethodName"];
      if (string.IsNullOrEmpty(methodName))
      {
        EndRequest("MethodName标记不能为空!");
      }
      Type type = this.GetType().BaseType;
      MethodInfo info = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
      if (info == null)
      {
        EndRequest("找不到合适的方法调用!");
      }        
      string data = info.Invoke(this, null) as string;
      EndRequest(data);
    }
    base.ProcessRequest(context);
  }
  private void EndRequest(string msg)
  {
    HttpResponse response = this.Context.Response;
    response.Write(msg);
    response.End();
  }
}
Copier après la connexion

  页面类:

public partial class Test1 : PageBase
{
  protected void Page_Load(object sender, EventArgs e)
  {
  }
  public string GetData()
  {
    return "213";
  }
}
Copier après la connexion

  前台代码:

function getData(){
  $.ajax({
    headers:{"AjaxFlag":"XHR","MethodName":"GetData"},
    success:function(data){
      $("#result").text(data);
    }
  });
}
Copier après la connexion

四、优化版页面基类

  上面的页面基类功能很少,而且通过反射这样调用的效率很低。这里优化一下:

  1.可以支持简单类型的参数。

    例如上面的GetData可以是:GetData(string name),通过函数元数据可以获取相关的参数,再根据请求的参数,就可以设置参数了。

  2.加入标记属性。

    只有被AjaxMethodAttribute标记的属性才能被外部调用。

  3.优化反射。

    利用缓存,避免每次都根据函数名称去搜索函数信息。

  标记属性:

public class AjaxMethodAttribute : Attribute
{
}
Copier après la connexion


  缓存对象:  

public class CacheMethodInfo
{
  public string MethodName { get; set; }
  public MethodInfo MethodInfo { get; set; }
  public ParameterInfo[] Parameters { get; set; }
}
Copier après la connexion


  基类代码:

public class PageBase : Page
{
  private static Hashtable _ajaxTable = Hashtable.Synchronized(new Hashtable());
  public override void ProcessRequest(HttpContext context)
  {      
    HttpRequest request = context.Request;
    if (string.Compare(request.Headers["AjaxFlag"],"XHR",true) == 0)
    {
      InvokeMethod(request.Headers["MethodName"]);
    }
    base.ProcessRequest(context);
  }
  /// <summary>
  /// 反射执行函数
  /// </summary>
  /// <param name="methodName"></param>
  private void InvokeMethod(string methodName)
  {
    if (string.IsNullOrEmpty(methodName))
    {
      EndRequest("MethodName标记不能为空!");
    }
    CacheMethodInfo targetInfo = TryGetMethodInfo(methodName);
    if (targetInfo == null)
    {
      EndRequest("找不到合适的方法调用!");
    }
    try
    {
      object[] parameters = GetParameters(targetInfo.Parameters);
      string data = targetInfo.MethodInfo.Invoke(this, parameters) as string;
      EndRequest(data);
    }
    catch (FormatException)
    {
      EndRequest("参数类型匹配发生错误!");
    }
    catch (InvalidCastException)
    {
      EndRequest("参数类型转换发生错误!");
    }
    catch (ThreadAbortException)
    {
    }
    catch (Exception e)
    {
      EndRequest(e.Message);
    }
  }
  /// <summary>
  /// 获取函数元数据并缓存
  /// </summary>
  /// <param name="methodName"></param>
  /// <returns></returns>
  private CacheMethodInfo TryGetMethodInfo(string methodName)
  {
    Type type = this.GetType().BaseType;
    string cacheKey = type.AssemblyQualifiedName;
    Dictionary<string, CacheMethodInfo> dic = _ajaxTable[cacheKey] as Dictionary<string, CacheMethodInfo>;
    if (dic == null)
    {
      dic = new Dictionary<string, CacheMethodInfo>();
      MethodInfo[] methodInfos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                    let ma = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false)
                    where ma.Length > 0
                    select m).ToArray();
      foreach (var mi in methodInfos)
      {
        CacheMethodInfo cacheInfo = new CacheMethodInfo();
        cacheInfo.MethodName = mi.Name;
        cacheInfo.MethodInfo = mi;
        cacheInfo.Parameters = mi.GetParameters();
        dic.Add(mi.Name, cacheInfo);
      }
      _ajaxTable.Add(cacheKey, dic);
    }
    CacheMethodInfo targetInfo = null;
    dic.TryGetValue(methodName, out targetInfo);
    return targetInfo;
  }
  /// <summary>
  /// 获取函数参数
  /// </summary>
  /// <param name="parameterInfos"></param>
  /// <returns></returns>
  private object[] GetParameters(ParameterInfo[] parameterInfos)
  {
    if (parameterInfos == null || parameterInfos.Length <= 0)
    {
      return null;
    }
    HttpRequest request = this.Context.Request;
    NameValueCollection nvc = null;
    string requestType = request.RequestType;
    if (string.Compare("GET", requestType, true) == 0)
    {
      nvc = request.QueryString;
    }
    else
    {
      nvc = request.Form;
    }
    int length = parameterInfos.Length;
    object[] parameters = new object[length];
    if (nvc == null || nvc.Count <= 0)
    {
      return parameters;
    }
    for (int i = 0; i < length; i++)
    {
      ParameterInfo pi = parameterInfos[i];
      string[] values = nvc.GetValues(pi.Name);
      object value = null;
      if (values != null)
      {
        if (values.Length > 1)
        {
          value = String.Join(",", values);
        }
        else
        {
          value = values[0];
        }
      }
      if (value == null)
      {
        continue;
      }
      parameters[i] = Convert.ChangeType(value, pi.ParameterType);
    }      
    return parameters;
  }
  private void EndRequest(string msg)
  {
    HttpResponse response = this.Context.Response;
    response.Write(msg);
    response.End();
  }
}
Copier après la connexion

  页面类:

public string GetData3(int i, double d, string str)
{
  string[] datas = new string[] { i.ToString(), d.ToString(), str };
  return "参数分别是:" + String.Join(",", datas);
}
Copier après la connexion


  前台代码:

function getData3(){
  $.ajax({
    headers:{"AjaxFlag":"XHR","MethodName":"GetData3"},
    data:{"i":1,"d":"10.1a","str":"hehe"},
    success:function(data){
      $("#result").text(data);
    }
  });
}
Copier après la connexion

五、总结

  上面的页面基类已经具备可以完成基本的功能,但它还不够好。主要有:

  1. 依附在页面基类。对于本来有页面基类的,无疑会变得更加复杂。我们希望把它独立开来,变成一个单独的组件。

  2. 效率问题。反射的效率是很低的,尤其在web这类应用程序上,更应该慎用。以动态执行函数为例,效率主要低在:a.根据字符串动态查找函数的过程。b.执行函数时,反射内部需要将参数打包成一个数组,再将参数解析到线程栈上;在调用前CLR还要检测参数的正确性,再判断有没有权限执行。上面的优化其实只优化了一半,也就是优化了查找的过程,而Invoke同样会有性能损失。当然,随着.net版本越高,反射的效率也会有所提升,但这种动态的东西,始终是用效率换取灵活性的。

  3.不能支持复杂参数。有时候参数比较多,函数参数一般会封装成一个对象类型。

  4. AjaxMethodAttribute只是一个空的标记属性。我们可以为它加入一些功能,例如,标记函数的名称、是否使用Session、缓存设置等都可以再这里完成。

  用过WebForm的朋友可能会提到AjaxPro组件,这是一个开源的组件,下一篇就通过源码了解这个组件,借鉴它的处理过程,并且分析它的优缺点。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

Une brève analyse de la différence entre json et jsonp et la conversion de format après l'obtention de données json via ajax

Analyse d'un exemple d'image de chargement asynchrone Ajax

Ajax envoyant et recevant des requêtes

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal