軽量ajaxコンポーネントの書き方01~Webフォームプラットフォームでの各種実装方法の比較~

亚连
リリース: 2018-05-24 14:57:19
オリジナル
1382 人が閲覧しました

この記事では、主に軽量の ajax コンポーネント 01 の書き方を紹介します - Web フォーム プラットフォーム上のさまざまな実装方法と比較して、必要な友人はそれを参照してください

はじめに

Asp.net WebForm と Asp.net MVC (MVC と呼ばれます) ) はどちらも Asp.net に基づく Web 開発フレームワークです。その 1 つは、MVC が http の本質に重点を置いているのに対し、WebForm はこの目的のために http を保護しようとすることです。 Windows フォーム アプリケーションの開発と同じように、イベント モデルに基づいてプログラミングできます。どちらにも独自の長所、短所、適用可能なシナリオがありますが、現在、多くの Asp.net 開発者にとって MVC が最初の選択肢となっています。

WebFormはAsp.netをベースにしているので、これらを利用してWebForm上でMVCのようなフレームワークを書くこともできます。 WebForm というと、多くの人はサーバー コントロール (ドラッグ コントロール!!!) を思い浮かべるでしょう。実際には、サーバー コントロールをまったく使用せず、MVC のような HTML に重点を置くこともできます。 WebForm がサーバー コントロールを放棄して HTML に重点を置きたい場合は、まず

タグを削除する必要があります。この runat サーバー フォームは PostBack メカニズムの基礎です。 html+css+js に戻るということは、Ajax リクエストの処理など、多くのことを自分で実装する必要があることを意味します。 MVC とは異なり、WebForm の初期設計ではサーバー コントロールをメイン コンポーネントとして使用します。これを使用しない場合は、その拡張性を利用するしかありません。

このシリーズは、WebFormプラットフォームに基づいて軽量のajaxコンポーネントを実装するもので、主に3つのパートに分かれています:

1. WebFormでのさまざまな実装方法を紹介します。

2. ajaxpro コンポーネントを分析します。

3. 独自の ajax コンポーネントを作成します。

1. Ajax の概要

非同期を使用すると、ページ全体を更新せずにサーバーにデータをリクエストまたは送信できます。複雑なページの場合、少量のデータをリクエストするためにページ全体をリロードするのは明らかに非効率です。Ajax はこの問題を解決するように設計されています。 ajax の中核は XmlHttpRequest オブジェクトで、これを通じてリクエストがテキスト形式でサーバーに送信されます。 XmlHttpRequest2.0以降はバイナリデータの送信もサポートされています。

Ajax セキュリティ: セキュリティ上の理由から、Ajax は同一オリジン ポリシーによって制限されています。つまり、同じドメインと同じポートからのリクエストのみにアクセスでき、クロスドメイン リクエストは拒否されます。もちろん、要件によっては、ドメイン間でリクエストを送信する必要がある場合があります。一般的に使用されるクロスドメイン処理方法には、CORS (クロスドメイン リソース共有) や JSONP (パラメトリック JSON) があります。

Ajaxデータ対話形式: AjaxコアオブジェクトXmlHttpRequestには「XML」という言葉がありますが、クライアントとサーバー間のデータ交換形式はxmlに限定されません。たとえば、現在ではjson形式が使用されることが多くなっています。

Ajax にも欠点があります。たとえば、検索エンジンのサポートはあまり優れていません。URL リソースの配置の本来の目的に反する場合があります。

2. Asp.net MVC プラットフォームでの ajax の使用

MVC では、Ajax がバックグラウンド メソッドを呼び出すのに非常に便利です。アクションの名前を指定するだけです。

フロントエンドコード:

<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>
ログイン後にコピー

バックエンドコード:

public class AjaxController : Controller
{
  public ActionResult GetData()
  {
    if(Request.IsAjaxRequest())
    {
      return Content("data");
    }
    return View();
  }
}
ログイン後にコピー

3. WebFormプラットフォームでのajaxの使用

3.1 サーバーベース制御パッケージまたはサード- partyコンポーネント

これは、ajaxツールキットなどのサーバーコントロール、またはFineUIなどのコンポーネントに基づいています。 Web フロントエンドは常に html+css+js で構成されますが、問題はそれをどのように生成するかです。ネイティブ プラグインを自分で作成することも、いくつかのフロントエンド プラグインを使用することもできますが、サーバー コントロールに基づくプラグインはバックグラウンドで生成されるため、通常は効率が低くなります。サーバー コンポーネントはフォアグラウンドで一連のプロキシを生成しますが、本質は同じですが、コントロールはこのプロセスをカプセル化するため、自分で記述する必要はありません。コントロールまたはサードパーティ コンポーネントに基づくモデルは、訪問数がそれほど多くないため、短期間で開発できるため、一部の管理システムでは非常に役立ちます。

3.2 ICallbackEventHandlerインターフェースに基づく

.netは、コールバックリクエストを処理するためのICallbackEventHandlerインターフェースを提供します。このインターフェイスは、ClientScriptManager を使用してリクエストを送受信するためのプロキシ スクリプトをフォアグラウンドで生成する必要があるため、

タグが必要です。

フロントエンドコード:

<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>
ログイン後にコピー

バックエンドコード:

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)
  {
  }
}
ログイン後にコピー

このメソッドには次の欠点があります:

1. 実装がより複雑で、各ページのロードイベントはスクリプトに応じて登録する必要があります。

2. フロントエンドはプロキシ用のスクリプトファイルを生成します。

3. 複雑なページインタラクションの場合、実装は非常に面倒です。

4. コールバックですが、この時点でもページオブジェクトは生成されています。

3.3 一般的な処理手順を使用する

  一般处理程序其实是一个实现了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);        
  }
}
ログイン後にコピー

  这种方式的优点是轻量、高效;缺点是对于交互多的需要定义许多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();
  }
}
ログイン後にコピー

  页面类:

public partial class Test1 : PageBase
{
  protected void Page_Load(object sender, EventArgs e)
  {
  }
  public string GetData()
  {
    return "213";
  }
}
ログイン後にコピー

  前台代码:

function getData(){
  $.ajax({
    headers:{"AjaxFlag":"XHR","MethodName":"GetData"},
    success:function(data){
      $("#result").text(data);
    }
  });
}
ログイン後にコピー

四、优化版页面基类

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

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

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

  2.加入标记属性。

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

  3.优化反射。

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

  标记属性:

public class AjaxMethodAttribute : Attribute
{
}
ログイン後にコピー


  缓存对象:  

public class CacheMethodInfo
{
  public string MethodName { get; set; }
  public MethodInfo MethodInfo { get; set; }
  public ParameterInfo[] Parameters { get; set; }
}
ログイン後にコピー


  基类代码:

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();
  }
}
ログイン後にコピー

  页面类:

public string GetData3(int i, double d, string str)
{
  string[] datas = new string[] { i.ToString(), d.ToString(), str };
  return "参数分别是:" + String.Join(",", datas);
}
ログイン後にコピー


  前台代码:

function getData3(){
  $.ajax({
    headers:{"AjaxFlag":"XHR","MethodName":"GetData3"},
    data:{"i":1,"d":"10.1a","str":"hehe"},
    success:function(data){
      $("#result").text(data);
    }
  });
}
ログイン後にコピー

五、总结

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

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

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

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

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

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

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

相关文章:

jsonとjsonpの違いと、ajaxでjsonデータを取得した後の形式変換の簡単な分析

Ajax非同期読み込みイメージのサンプル分析

Ajaxリクエストの送受信

以上が軽量ajaxコンポーネントの書き方01~Webフォームプラットフォームでの各種実装方法の比較~の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート