首页 web前端 js教程 编写轻量ajax组件01-与webform平台上的各种实现方式比较

编写轻量ajax组件01-与webform平台上的各种实现方式比较

May 24, 2018 pm 02:57 PM
ajax web

这篇文章主要介绍了编写轻量ajax组件01-与webform平台上的各种实现方式比较,需要的朋友可以参考下

前言

  Asp.net WebForm 和 Asp.net MVC(简称MVC) 都是基于Asp.net的web开发框架,两者有很大的区别,其中一个就是MVC更加注重http本质,而WebForm试图屏蔽http,为此提供了大量的服务器控件和ViewState机制,让开发人员可以像开发Windows Form应用程序一样,基于事件模型去编程。两者各有优缺点和适用情景,但MVC现在是许多Asp.net开发者的首选。

  WebForm是建立在Asp.net的基础上的,Asp.net提供了足够的扩展性,我们也可以利用这些在WebForm下编写像MVC一样的框架,这个有机会再写。说到WebForm很多人就会联想到服务器控件(拖控件!!!),其实不然,我们也可以完全不使用服务器控件,像MVC那样关注html。WebForm要抛弃服务器控件,集中关注html,首先就要将

标签去掉,这个runat server 的form 是其PostBack机制的基础。既然我们要回归到html+css+js,那么意味着许多东西都要自己实现,例如处理Ajax请求。不像MVC那样,WebForm开始的设计就将服务器控件作为主要组成部分,如果不使用它,那么只能利用它的扩展性去实现。

  本系列就是实现一个基于WebForm平台的轻量级ajax组件,主要分为三个部分:

  1. 介绍WebForm下各种实现方式。

  2. 分析ajaxpro组件。

  3. 编写自己的ajax组件。

一、Ajax简介

  异步允许我们在不刷新整个页面的情况下,像服务器请求或提交数据。对于复杂的页面,为了请求一点数据而重载整个页面显然是很低效的,ajax就是为了解决这个问题的。ajax的核心是XmlHttpRequest对象,通过该对象,以文本的形式向服务器提交请求。XmlHttpRequest2.0后,还支持提交二进制数据。

  ajax安全:出于安全考虑,ajax受同源策略限制;也就是只能访问同一个域、同一个端口的请求,跨域请求会被拒绝。当然有时候需求需要跨域发送请求,常用的跨域处理方法有CORS(跨域资源共享)和JSONP(参数式JSON)。

  ajax数据交互格式:虽然Ajax核心对象XmlHttpRequest有"XML"字眼,但客户端与服务器数据交换格式不局限于xml,例如现在更多是使用json格式。  

  ajax 也是有缺点的。例如对搜索引擎的支持不太好;有时候也会违背url资源定位的初衷。

二、Asp.net MVC 平台下使用ajax

  在MVC里,ajax调用后台方法非常方便,只需要指定Action的名称即可。

  前台代码:

<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();
  }
}
登录后复制

三、WebForm 平台下使用ajax

  3.1 基于服务器控件包或者第三方组件

  这是基于服务器控件的,例如ajax toolkit工具包,或者像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. 实现起来较复杂,每个页面Load事件都要去注册相应的脚本。

  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-与webform平台上的各种实现方式比较的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

解决jQuery AJAX请求遇到403错误的方法 解决jQuery AJAX请求遇到403错误的方法 Feb 20, 2024 am 10:07 AM

标题:解决jQueryAJAX请求出现403错误的方法及代码示例403错误是指服务器禁止访问资源的请求,通常会导致出现这个错误的原因是请求缺少权限或者被服务器拒绝。在进行jQueryAJAX请求时,有时候会遇到这种情况,本文将介绍如何解决这个问题,并提供代码示例。解决方法:检查权限:首先要确保请求的URL地址是正确的,同时验证是否有足够的权限来访问该资

解决jQuery AJAX请求403错误的方法 解决jQuery AJAX请求403错误的方法 Feb 19, 2024 pm 05:55 PM

jQuery是一个流行的JavaScript库,用于简化客户端端的开发。而AJAX则是在不重新加载整个网页的情况下,通过发送异步请求和与服务器交互的技术。然而在使用jQuery进行AJAX请求时,有时会遇到403错误。403错误通常是服务器禁止访问的错误,可能是由于安全策略或权限问题导致的。在本文中,我们将讨论如何解决jQueryAJAX请求遭遇403错误

如何使用Ajax从PHP方法中获取变量? 如何使用Ajax从PHP方法中获取变量? Mar 09, 2024 pm 05:36 PM

使用Ajax从PHP方法中获取变量是Web开发中常见的场景,通过Ajax可以实现页面无需刷新即可动态获取数据。在本文中,将介绍如何使用Ajax从PHP方法中获取变量,并提供具体的代码示例。首先,我们需要编写一个PHP文件来处理Ajax请求,并返回所需的变量。下面是一个简单的PHP文件getData.php的示例代码:

如何解决jQuery AJAX报错403的问题? 如何解决jQuery AJAX报错403的问题? Feb 23, 2024 pm 04:27 PM

如何解决jQueryAJAX报错403的问题?在开发网页应用程序时,经常会使用jQuery来发送异步请求。然而,有时候在使用jQueryAJAX时可能会遇到错误代码403,表示服务器禁止访问。这种情况通常是由服务器端的安全设置所导致的,但可以通过一些方法来解决这个问题。本文将介绍如何解决jQueryAJAX报错403的问题,并提供具体的代码示例。一、使

如何从驾驶舱Web用户界面启用管理访问 如何从驾驶舱Web用户界面启用管理访问 Mar 20, 2024 pm 06:56 PM

Cockpit是一个面向Linux服务器的基于Web的图形界面。它主要是为了使新用户/专家用户更容易管理Linux服务器。在本文中,我们将讨论Cockpit访问模式以及如何从CockpitWebUI切换Cockpit的管理访问。内容主题:驾驶舱进入模式查找当前驾驶舱访问模式从CockpitWebUI启用Cockpit的管理访问从CockpitWebUI禁用Cockpit的管理访问结论驾驶舱进入模式驾驶舱有两种访问模式:受限访问:这是驾驶舱的默认访问模式。在这种访问模式下,您不能从驾驶舱Web用户

PHP 与 Ajax:构建一个自动完成建议引擎 PHP 与 Ajax:构建一个自动完成建议引擎 Jun 02, 2024 pm 08:39 PM

使用PHP和Ajax构建自动完成建议引擎:服务器端脚本:处理Ajax请求并返回建议(autocomplete.php)。客户端脚本:发送Ajax请求并显示建议(autocomplete.js)。实战案例:在HTML页面中包含脚本并指定search-input元素标识符。

PHP在Web开发中是属于前端还是后端? PHP在Web开发中是属于前端还是后端? Mar 24, 2024 pm 02:18 PM

PHP在Web开发中是属于后端。PHP是一种服务器端脚本语言,主要用于处理服务器端的逻辑,生成动态网页内容。与前端技术相比,PHP更多地用于与数据库交互、处理用户请求以及生成页面内容等后端操作。接下来通过具体的代码示例来说明PHP在后端开发中的应用。首先,我们来看一个简单的PHP代码示例,用于连接数据库并查询数据:

PHP 与 Ajax:创建动态加载内容的解决方案 PHP 与 Ajax:创建动态加载内容的解决方案 Jun 06, 2024 pm 01:12 PM

Ajax(异步JavaScript和XML)允许在不重新加载页面情况下添加动态内容。使用PHP和Ajax,您可以动态加载产品列表:HTML创建一个带有容器元素的页面,Ajax请求加载数据后将数据添加到该元素中。JavaScript使用Ajax通过XMLHttpRequest向服务器发送请求,从服务器获取JSON格式的产品数据。PHP使用MySQL从数据库查询产品数据,并将其编码为JSON格式。JavaScript解析JSON数据,并将其显示在页面容器中。点击按钮触发Ajax请求,加载产品列表。

See all articles