搜尋
首頁web前端js教程編寫輕量級ajax組件(圖文詳解)

這次為大家帶來編寫輕量級ajax組件(圖文詳解),編寫輕量級ajax組件的注意事項有哪些,下面就是實戰案例,一起來看一下。

透過先前的介紹,我們知道要執行頁面物件的方法,核心就是反射,是從請求取得參數並執行指定方法的過程。實際上這和asp.net mvc框架的核心思想很類似,它會解析url,從中獲取controller和action名稱,然後激活controller對象,從請求獲取action參數並執action。在web form平台上,我們把方法寫在.aspx.cs中,要實現的就是在頁面物件還未產生的情況下,執行指定的方法,然後回傳結果。

  我們先看實作後幾個呼叫例子,這些功能也可以組合使用:      

 [AjaxMethod]
    public void Test1(int index)
    {
      //简单调用
    }
    [AjaxMethod]
    public string Test2(Test test)
    {
      return "参数为一个Test实例";
    }
    [AjaxMethod(OutputCache = 20)]
    public string Test3(int index)
    {
      return "输出结果缓存20秒";
    }
    [AjaxMethod(ServerCache = 20)]
    public string Test4()
    {
      return "在服务端缓存20秒";
    }
    [AjaxMethod(SessionState=SessionState.None)]
    public void Test5()
    {
      //Session未被加载
    }
    [AjaxMethod(SessionState = SessionState.ReadOnly)]
    public void Test6()
    {
      //Session只能读不能写
    }
    [AjaxMethod(SessionState = SessionState.ReadWrite)]
    public void Test7()
    {
      //Session可以读写
    }
    [AjaxMethod(IsAsync = true)]
    public void Test8()
    {
      //异步调用
    }

  前面我們已經熟悉基本的執行流程,現在直接進入主題。

Ajax約定

  通常現在主流瀏覽器在使用ajax發送非同步請求時,請求頭都會帶上一個:X-Requested-With:XMLHttpRequest 的標記。我們也可以直接透過這個標記來判斷是不是ajax請求,不過專案中可能有用其它的元件,為了不互相影響,我們加入一個自訂的請求頭。這裡為:

internal static class AjaxConfig
 {
  /// <summary>
  /// 请求头Ajax标记键
  /// </summary>
  public const string Key = "AjaxFlag";
  /// <summary>
  /// 请求头Ajax标记值
  /// </summary>
  public const string Value = "XHR";
  /// <summary>
  /// 请求头Ajax方法标记
  /// </summary>
  public const string MethodName = "";
 }

  意思是如果http 的請求頭包含一個 AjaxFlag : XHR,就是我們要處理的。另外http header的MethodName就表示我們要執行的方法的名稱。

AjaxMethodAttribute標記屬性

  標記屬性是給反射用的,在這裡定義我們需要的一些功能。我們希望有:

  1. 可以設定Session狀態

  2. 支援非同步Handler

  3. 支援Get快取

  4. 支援服務端快取

  定義如下,用AttributeUsag標記該標記只能用於方法上。

  /// <summary>
  /// ajax方法标记属性
  /// </summary>
  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
  public class AjaxMethodAttribute : Attribute
  {
    public AjaxMethodAttribute()
    {      
    }
    private SessionState _sessionState = SessionState.None;
    private int _outputCache = 0;
    private int _serverCache = 0;
    private ContentType _contentType = ContentType.Plain;
    private bool _isUseAsync = false;
    /// <summary>
    /// session状态
    /// </summary>
    public SessionState SessionState 
    {
      get { return _sessionState; }
      set { _sessionState = value; }
    }
    /// <summary>
    /// 客户端缓存时间,以秒为单位。该标记只对get请求有效
    /// </summary>
    public int OutputCache 
    {
      get { return _outputCache; }
      set { _outputCache = value; }
    }
    /// <summary>
    /// 服务端缓存时间,以秒为单位
    /// </summary>
    public int ServerCache 
    {
      get { return _serverCache; }
      set { _serverCache = value; }
    }    
    /// <summary>
    /// 输出类型(默认为text/plain)
    /// </summary>
    public ContentType ContentType 
    {
      get { return _contentType; }
      set { _contentType = value; }
    }
    /// <summary>
    /// 使用启用异步处理
    /// </summary>
    public bool IsAsync 
    {
      get { return _isUseAsync; }
      set { _isUseAsync = value; }
    }
  }
  /// <summary>
  /// Session状态
  /// </summary>
  public enum SessionState
  {
    None,
    ReadOnly,
    ReadWrite    
  }
  /// <summary>
  /// 输出内容类型
  /// </summary>
  public enum ContentType
  {
    Plain,
    Html,
    XML,
    Javascript,
    JSON
  }

各種處理程序和AjaxHandlerFactory

  依照上一篇的說法,具體的Handler主要分為兩類,異步和非非同步;這兩類下,對於Session的狀態又有3三種,不支援、只支援讀(實作IReadOnlySessionState介面)、支援讀寫(實作IRequiresSessionState介面)。 IReadOnlySessionState和IRequiresSessionState都只是標記介面(無任何方法,其實應該用標記屬性實作比較合理)。非同步的Handler需要實作IHttpAsyncHandler接口,該接口又實作了IHttpHandler。 Handler的ProcessRequest方法(或BeginProcessRequest)就是我們要執行方法的地方。定義如下:

  非異步狀態的Handler:

 //不支持Session
  internal class SyncAjaxHandler : IHttpHandler
  {
    private Page _page;
    private CacheMethodInfo _cacheMethodInfo;
    internal SyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo)
    {
      _page = page;
      _cacheMethodInfo = cacheMethodInfo;
    }
    public void ProcessRequest(HttpContext context)
    {
      //执行方法(下面详细介绍)
      Executor.Execute(_page, context, _cacheMethodInfo);
    }
    public bool IsReusable
    {
      get { return false; }
    }
    public static SyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state)
    {
      switch (state)
      {
        case SessionState.ReadOnly:
          return new SyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo);
        case SessionState.ReadWrite:
          return new SyncAjaxSessionHandler(page, cacheMethodInfo);
        default:
          return new SyncAjaxHandler(page, cacheMethodInfo);
      }
    }
  }
  //支持只读Session
  internal class SyncAjaxSessionReadOnlyHandler : SyncAjaxHandler, IReadOnlySessionState
  {
    internal SyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }
  //支持读写Session
  internal class SyncAjaxSessionHandler : SyncAjaxHandler, IRequiresSessionState
  {
    internal SyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }

  異步狀態的Handler:

 //不支持Session
  internal class ASyncAjaxHandler : IHttpAsyncHandler, IHttpHandler
  {
    private Page _page;
    private CacheMethodInfo _cacheMethodInfo;
    internal ASyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo)
    {
      _page = page;
      _cacheMethodInfo = cacheMethodInfo;
    }
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
      //执行方法(下面详细介绍)
      Action<page> action = new Action<page>(Executor.Execute);
      IAsyncResult result = action.BeginInvoke(_page, context, _cacheMethodInfo, cb, action);
      return result;
    }
    public void EndProcessRequest(IAsyncResult result)
    {
      Action<page> action = result.AsyncState as Action<page>;
      action.EndInvoke(result);
    }
    public void ProcessRequest(HttpContext context)
    {
      throw new NotImplementedException();
    }
    public bool IsReusable
    {
      get { return false; }
    }
    public static ASyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state)
    {
      switch (state)
      {
        case SessionState.ReadOnly:
          return new ASyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo);
        case SessionState.ReadWrite:
          return new ASyncAjaxSessionHandler(page, cacheMethodInfo);
        default:
          return new ASyncAjaxHandler(page, cacheMethodInfo);
      }
    }
  }
  //支持只读Session
  internal class ASyncAjaxSessionReadOnlyHandler : ASyncAjaxHandler, IReadOnlySessionState
  {
    internal ASyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }
  //支持读写Session
  internal class ASyncAjaxSessionHandler : ASyncAjaxHandler, IRequiresSessionState
  {
    internal ASyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)
      : base(page, cacheMethodInfo)
    {
    }
  }</page></page></page></page>

  AjaxHandlerFactory實現了IHandlerFactory接口,用來根據請求生成具體的Handler,它需要在web.config進行註冊使用。 AjaxHandlerFactory的GetHandler是我們攔截請求的第一步。透過請求頭的AjaxFlag:XHR來判斷是否需要我們處理,如果是,則建立一個Handler,否則按照普通的方式進行。由於我們的方法是寫在.aspx.cs內的,我們的請求是.aspx後綴的,也就是頁面(Page,實現了IHttpHandler)類型,Page是透過PageHandlerFactory創建的,PageHandlerFactory也實現了IHandlerFactory接口,表示它是用來創建處理程序的。所以我們需要用PageHandlerFactory來建立一個IHttpHandler,不過PageHandlerFactory的建構子是protected internal類型的,我們無法直接new一個,所以需要透過一個CommonPageHandlerFactory繼承它來實現。

  透過PageHandlerFactory取得Page後,結合方法名稱,我們就可以反射取得AjaxMethodAttribute標記屬性了。然後根據它的相關屬性生成具體的Handler。具體程式碼如下:

 internal class CommonPageHandlerFactory : PageHandlerFactory { }
  internal class AjaxHandlerFactory : IHttpHandlerFactory
  {
    public void ReleaseHandler(IHttpHandler handler)
    {
    }
    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
      HttpRequest request = context.Request;
      if (string.Compare(request.Headers[AjaxConfig.Key], AjaxConfig.Value, true) == 0)
      {
        //检查函数标记
        string methodName = request.Headers[AjaxConfig.MethodName];
        if (methodName.IsNullOrEmpty())
        {
          Executor.EndCurrentRequest(context, "方法名称未正确指定!");
          return null;
        }
        try
        {          
          CommonPageHandlerFactory ajaxPageHandler = new CommonPageHandlerFactory();
          IHttpHandler handler = ajaxPageHandler.GetHandler(context, requestType, url, pathTranslated);
          Page page = handler as Page;
          if (page == null)
          {
            Executor.EndCurrentRequest(context, "处理程序类型必须是aspx页面!");
            return null;
          }
          return GetHandler(page, methodName, context);
        }
        catch
        {
          Executor.EndCurrentRequest(context, url + " 不存在!");
          return null;
        }
      }
      if (url.EndsWith(".aspx", StringComparison.CurrentCultureIgnoreCase))
      {
        CommonPageHandlerFactory orgPageHandler = new CommonPageHandlerFactory();
        return orgPageHandler.GetHandler(context, requestType, url, pathTranslated);
      }
      return null;
    }
    /// <summary>
    /// 获取自定义处理程序
    /// </summary>
    /// <param>处理页面
    /// <param>处理方法
    /// <param>当前请求
    private IHttpHandler GetHandler(Page page, string methodName, HttpContext context)
    {
      //根据Page和MethodName进行反射,获取标记属性(下面详细介绍)
      CacheMethodInfo methodInfo = Executor.GetDelegateInfo(page, methodName);
      if (methodInfo == null)
      {
        Executor.EndCurrentRequest(context, "找不到指定的Ajax方法!");
        return null;
      }
      AjaxMethodAttribute attribute = methodInfo.AjaxMethodAttribute;
      if (attribute.ServerCache > 0)
      {
        //先查找缓存
        object data = CacheHelper.TryGetCache(context);
        if (data != null)
        {
          Executor.EndCurrentRequest(context, data);
          return null;
        }
      }
      if (attribute.IsAsync)
      {
        //异步处理程序
        return ASyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState);
      }
      return SyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState);
    }
  }

  上面的CacheMethodInfo是用于缓存调用方法的相关信息的,第一篇我们有提到过优化缓存的一些方法,其中就包括缓存+委托。但这里我们并不直接缓存方法的MethodInfo,因为缓存MethodInfo的话,需要通过Invoke去执行,这样的效率比较低。这里我缓存的是方法的委托,该委托的签名为:Func,该委托的返回值为object类型,表示可以返回任意的类型(我们可以在组件内部进行处理,例如如果是引用类型(非string),就将其序列化为json,但这里并没有实现)。该委托接收两个参数,第一个参数是方法所属的对象,如果是静态方法就是null;第二个参数是方法的参数,定义为object[]表示可以接收任意类型的参数。通过委托执行方法,与直接调用方法的效率差别就不是很大(对委托不熟悉的朋友可以参见:委托)。CacheMethodInfo的定义如下:

 /// <summary>
  /// 缓存方法信息  
  /// </summary>
  sealed class CacheMethodInfo
  {
    /// <summary>
    /// 方法名称
    /// </summary>
    public string MethodName { get; set; }
    /// <summary>
    /// 方法委托
    /// </summary>
    public Func<object> Func { get; set; }
    /// <summary>
    /// 方法参数
    /// </summary>
    public ParameterInfo[] Parameters { get; set; }
    /// <summary>
    /// Ajax标记属性
    /// </summary>
    public AjaxMethodAttribute AjaxMethodAttribute { get; set; }
  }</object>

核心方法

1. Eexcutor.GetDelegateInfo 获取方法相关信息

  该方法用于遍历页面类,获取所有AjaxMethodAttribute标记的方法信息,生成一个CacheMethodInfo对象,包括标记信息、方法名称、参数信息,以及最重要的方法委托。该对象会缓存在一个哈希表中,下次获取时,直接从内存获得。

 /// <summary>
    /// 获取页面标记方法信息
    /// </summary>
    /// <param>页面对象
    /// <param>方法名称
    internal static CacheMethodInfo GetDelegateInfo(Page page, string methodName)
    {
      if (page == null)
      {
        throw new ArgumentNullException("page");
      }
      Type type = page.GetType();
      //ajaxDelegateTable是一个Hashtable
      Dictionary<string> dic = ajaxDelegateTable[type.AssemblyQualifiedName] as Dictionary<string>;
      if (dic == null)
      {
        dic = new Dictionary<string>();
        //遍历页面的所有MethodInfo
        IEnumerable<cachemethodinfo> infos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                           let ca = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false).FirstOrDefault()
                           where ca != null
                           select new CacheMethodInfo
                           {
                             //方法标记属性
                             AjaxMethodAttribute = ca as AjaxMethodAttribute,
                             //方法名称
                             MethodName = m.Name,
                             //方法参数信息
                             Parameters = m.GetParameters()
                           });
        if (infos.IsNullOrEmpty())
        {
          return null;
        }          
        for (int i = 0, length = infos.Count(); i <p style="text-align: left;">  获取方法的委托的是通过一个ReflectionUtil获得的,该类主要用来优化反射,它通过Expression,可以将MethodInfo编译成Func<object>委托,为Type编译一个Func<object>委托,用于创建实例对象。</object></object></p>
<p style="text-align: left;">  通过Expression优化反射</p>
<p style="text-align: left;">  Expression(表达式树)允许我们将代码逻辑以表达式的形式存储在树状结构里,然后在运行时去动态解析,实现动态编辑和执行代码。熟悉ORM框架的朋友对Expression肯定很熟悉,因为大部分方法都有一个Expression<tdelegate>类型的参数。访问<a href="http://www.php.cn/code/5790.html" target="_blank">关系型数据库</a>的本质还是sql语句,orm的工作就是为开发人员屏蔽这个过程,以<a href="http://www.php.cn/code/6286.html" target="_blank">面向对象</a>的方式去读写数据库,而不是自己编写sql语句。例如,Users.Where(u => u.Age > 18) 就可查询年龄大于18的用户。这里不对应用在orm的过程进行详解,下面我们介绍如何用Expression并利用它来生成委托。</tdelegate></p>
<p style="text-align: left;">  .net定义了许多表达式类型,这些类型都派生自Expression,Expression是一个抽象类,而且是一个工厂类,所有类型的表达式都通过它来创建。如图:</p>
<p style="text-align: left;">  先看一个 1 * 2 + 2 例子,我们用表达树来描述来描述它:</p>
<pre class="brush:php;toolbar:false">/*
       * a * b + 2 
       */
      /*
      直接操作
      int a = 1, b = 2;
      int result = a * 2 + 2;
      */
      /*
      通过委托调用
      Func<int> func = new Func<int>((a, b) => { return a * b + 2; });
      func(1, 2);
      */
      /*通过Expression调用*/
      //定义两个参数
      ParameterExpression pe1 = Expression.Parameter(typeof(int), "a");
      ParameterExpression pe2 = Expression.Parameter(typeof(int), "b");
      //定义一个常量
      ConstantExpression constExpression = Expression.Constant(2);      
      //参数数组
      ParameterExpression[] parametersExpression = new ParameterExpression[]{pe1,pe2};
      //一个乘法运算
      BinaryExpression multiplyExpression = Expression.Multiply(pe1, pe2);
      //一个加法运算
      BinaryExpression unaryExpression = Expression.Add(multiplyExpression, constExpression);
      //将上面的表达式转换为一个委托表达式
      LambdaExpression lambdaExpression = Expression.Lambda<func>>(unaryExpression, parametersExpression);
      //将委托编译成可执行代码
      Func<int> func = lambdaExpression.Compile() as Func<int>;
      Console.WriteLine(func(1, 2));</int></int></func></int></int>

  可以看到我们最终将其编译为一个具体类型的委托了。下面看我们真正用到的方法是如何实现的,代码如下:

 public static Func<object> GetMethodDelegate(MethodInfo methodInfo)
    {
      if (methodInfo == null)
      {
        throw new ArgumentNullException("methodInfo");
      }
      //定义参数表达式,它表示委托的第一个参数
      ParameterExpression instanceExp = Expression.Parameter(typeof(object), "instance");
      //定义参数表达式,它表示委托的第二个参数
      ParameterExpression paramExp = Expression.Parameter(typeof(object[]), "parameters");
      //获取方法的参数信息数组
      ParameterInfo[] paramInfos = methodInfo.GetParameters();
      //参数表达式集合
      List<expression> paramExpList = new List<expression>();
      int length = paramInfos.Length;
      for (int i = 0; i > lambda = Expression.Lambda<action>>(methodCall, instanceExp, paramExp);
        Action<object> action = lambda.Compile();
        return (instance, parameters) =>
        {
          action(instance, parameters);
          return null;
        };
      }
      else
      {
        UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
        Expression<func>> lambda = Expression.Lambda<func>>(castMethodCall, instanceExp, paramExp);
        return lambda.Compile();
      }
    }</func></func></object></action></expression></expression></object>

  具体代码都有注释解释,最终我们获得了一个Func类型的委托,它会作为CacheMethodInfo的属性进行缓存。有兴趣测试反射性能的朋友,也不妨去测试对比一下这几种方式执行的效率差别:1.直接执行方法 2.Emit 3. 缓存+委托 4.Delegate.DynamicInvoke。

2. Executor.Execute 执行委托

  在执行委托前,我们需要先从请求获取参数,映射到方法。参数可以是简单的类型,如 string Test(int i,int j); 也可以是一个对象,如 string Test(User user); 如果是 string Test(User user1, User user2) 也行,提交参数时只需要加上 user1或 user2 前缀即可,例如 user1.Name,user2.Name。这里没有支持更多的匹配方式,像mvc,它还支持嵌套类型等等,这些可以自己去实现。如果参数是一个对象,我们可能需要为它的字段进行赋值,也可能为它的属性进行赋值。这里我们定义一个DataMember,用来表示字段或属性的父类。如:

  internal abstract class DataMember
  {
    public abstract string Name { get; }
    public abstract Type MemberType { get; }
    public abstract void SetValue(object instance,object value);
    public abstract object GetValue(object instance);
  }

  接着定义属性类型PropertyMember和字段类型FieldMember,分别继承了DataMember。

  PropertyMember定义:

  internal class PropertyMember : DataMember
  {
    private PropertyInfo property;
    public PropertyMember(PropertyInfo property)
    {
      if (property == null)
      {
        throw new ArgumentNullException("property");
      }
      this.property = property;
    }
    public override void SetValue(object instance, object value)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      this.property.SetValue(instance, value, null);
    }
    public override object GetValue(object instance)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      return this.property.GetValue(instance,null);
    }
    public override string Name
    {
      get { return this.property.Name; }
    }
    public override Type MemberType
    {
      get { return this.property.PropertyType; }
    }
  }

  FieldMember定义:

 internal class FieldMember : DataMember
  {
    private FieldInfo field;
    public FieldMember(FieldInfo field)
    {
      if (field == null)
      {
        throw new ArgumentNullException("field");
      }
      this.field = field;
    }
    public override void SetValue(object instance, object value)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      this.field.SetValue(instance, value);
    }
    public override object GetValue(object instance)
    {
      if (instance == null)
      {
        throw new ArgumentNullException("instance");
      }
      return this.field.GetValue(instance);
    }
    public override string Name
    {
      get { return this.field.Name;}
    }
    public override Type MemberType
    {
      get { return this.field.FieldType; }
    }
  }

  定义一个DataMemberManager,用来遍历Type,获取所有字段和属性的,实现如下:

 internal static class DataMemberManager
  {
    /// <summary>
    /// 获取实例字段/属性集合
    /// </summary>
    /// <param>类型
    /// <returns></returns>
    public static List<datamember> GetDataMember(Type type)
    {
      if (type == null)
      {
        throw new ArgumentNullException("type");
      }
      IEnumerable<propertymember> propertyMembers = from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                         select new PropertyMember(property);
      IEnumerable<fieldmember> fieldMembers = from field in type.GetFields(BindingFlags.Instance | BindingFlags.Public)
                       select new FieldMember(field);
      List<datamember> members = new List<datamember>();
      foreach(var property in propertyMembers)
      {
        members.Add(property);
      }
      foreach (var field in fieldMembers)
      {
        members.Add(field);
      }
      return members;
    }
  }</datamember></datamember></fieldmember></propertymember></datamember>

  在前面我们定义的Handler的ProcessRequest方法中,我们调用了Executor.Execute,该方法用于执行委托,实现如下:

 /// <summary>
    /// 核心函数,执行Handler的方法
    /// </summary>
    /// <param>页面对象
    /// <param>请求上下文
    /// <param>缓存方法原数据
    internal static void Execute(Page page, HttpContext context, CacheMethodInfo methodInfo)
    {
      if (page == null)
      {
        throw new ArgumentNullException("page");
      }
      try
      {
        if (methodInfo != null)
        {
          HttpRequest request = context.Request;
          object[] parameters = GetParametersFromRequest(request, methodInfo.Parameters);
          object data = methodInfo.Func(page, parameters);
          int serverCache = methodInfo.AjaxMethodAttribute.ServerCache;
          if (serverCache > 0)
          {
            CacheHelper.Insert(context, methodInfo.AjaxMethodAttribute.ServerCache, data);
          }
          EndCurrentRequest(context, data, methodInfo.AjaxMethodAttribute.OutputCache);
        }
        else
        {
          EndCurrentRequest(context, "找不到合适的Ajax方法!");
        }
      }
      catch (FormatException)
      {
        EndCurrentRequest(context, "调用方法匹配到无效的参数!");
      }
      catch (InvalidCastException)
      {
        EndCurrentRequest(context, "参数转换出错!");
      }
      catch (System.Threading.ThreadAbortException)
      {
        //do nothing
      }
      catch (Exception ex)
      {
        EndCurrentRequest(context, ex.Message);
      }
    }

  CacheMethodInfo我们已经获得了,现在只要获得参数我们就可以执行方法。

  GetParameterFromRequest用于从请求获取object[]参数数组。根据上面所说的,如果参数是一个简单类型,那么直接进行转换;如果是实例对象,那么我们先要创建new一个实例对象,然后为其字段或属性赋值。实现如下:

 /// <summary>
    /// 从请求获取参参数
    /// </summary>
    /// <param>HttpRequest
    ///<param>参数信息
    /// <returns>参数数组</returns>
    private static object[] GetParametersFromRequest(HttpRequest request, ParameterInfo[] parameters)
    {
      if (parameters.IsNullOrEmpty())
      {
        return null;
      }
      int length = parameters.Length;
      object[] realParameters = new object[length];
      for (int i = 0; i <p style="text-align: left;">  ModelUtil会从Http Request获取参数,并进行<a href="http://www.php.cn/php/php-tp-conversion.html" target="_blank">类型转换</a>处理:</p><pre class="brush:php;toolbar:false"> internal static class ModelUtil
  {
    /// <summary>
    /// 缓存构造函数
    /// </summary>
    private static Hashtable constructorTable = Hashtable.Synchronized(new Hashtable());
 
    /// <summary>
    /// 根据名称从HttpRequest获取值
    /// </summary>
    /// <param>HttpRequest
    /// <param>键名称
    /// <param>参数类型
    /// <returns></returns>
    public static object GetValue(HttpRequest request, string name, Type type)
    {
      string[] values = null;
      if (string.Compare(request.RequestType, "POST", true) == 0)
      {
        values = request.Form.GetValues(name);
      }
      else
      {
        values = request.QueryString.GetValues(name);
      }
      if (values.IsNullOrEmpty())
      {
        return null;
      }
      string data = values.Length == 1 ? values[0] : string.Join(",", values);
      return Convert.ChangeType(data, type);
    }
    /// <summary>
    /// 创建实例对象
    /// </summary>
    /// <param>实例类型
    /// <returns></returns>
    public static object CreateModel(Type type)
    {
      if (type == null)
      {
        throw new ArgumentNullException("type");
      }
      Func<object> func = constructorTable[type.AssemblyQualifiedName] as Func<object>;
      if (func == null)
      {  
        func = ReflectionUtil.GetConstructorDelegate(type);
        constructorTable[type.AssemblyQualifiedName] = func;
      }
      if (func != null)
      {
        return func();
      }
      return null;
    }
    /// <summary>
    /// 填充模型
    /// </summary>
    /// <param>HttpRequest
    /// <param>键名称
    /// <param>参数类型
    /// <parparam>实例对象</parparam>
    public static void FillModelByRequest(HttpRequest request, string name, Type type, object model)
    {
      if (model == null)
      {
        return;
      }
      IEnumerable<datamember> members = DataMemberManager.GetDataMember(type);
      if (members.IsNullOrEmpty())
      {
        return;
      }
      object value = null;
      foreach (DataMember member in members)
      {
        value = GetValue(request, string.Format("{0}.{1}", name, member.Name), member.MemberType);
        value = value ?? GetValue(request, member.Name, member.MemberType);
        member.SetValue(model, value);
      }
    }
  }</datamember></object></object>

  如果是引用类型,需要通过构造函数创建对象,像前面用于,这里我们也用Expression来构建一个Func类型的委托来优化,它调用了ReflectionUtil.GetConstructorDelegate方法。实现如下:

  /// <summary>
    /// 获取构造函数委托
    /// </summary>
    /// <param>实例类型
    /// <returns></returns>
    public static Func<object> GetConstructorDelegate(Type type)
    {
      if (type == null)
      {
        throw new ArgumentNullException("type");
      }
      ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
      if (ci == null)
      {
        throw new MissingMemberException("类型必须有一个无参public构造函数!");
      }
      NewExpression newExp = Expression.New(type);
      Expression<func>> lambda = Expression.Lambda<func>>(newExp);
      return lambda.Compile();
    }</func></func></object>

  最后再输出结果时,如果是Get请求,并且需要缓存,我们还需要设置一下Response.Cache。如下:

    private static void EndRequest(HttpContext context, object data, int outPutCache, ContentType contentType)
    {
      HttpResponse response = context.Response;
      if (outPutCache != 0)
      {
        if (string.Compare(context.Request.HttpMethod, "GET", true) == 0)
        {
          if (outPutCache > 0)
          {
            response.Cache.SetCacheability(HttpCacheability.Public);
            response.Cache.SetMaxAge(new TimeSpan(0, 0, outPutCache));
            response.Cache.SetExpires(DateTime.Now.AddSeconds(outPutCache));
          }
          else
          {
            response.Cache.SetCacheability(HttpCacheability.NoCache);
            response.Cache.SetNoStore();
          }
        }
      }
      response.ContentType = GetContentType(contentType);
      response.ContentEncoding = System.Text.Encoding.UTF8;
      if (data != null)
      {
        response.Write(data);
      }
      response.End();
    }

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Ajax请求async有哪些方式?应该如何使用

FileUpload实现单个文件的上传

以上是編寫輕量級ajax組件(圖文詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C/C在JavaScript口譯員和編譯器中的作用C/C在JavaScript口譯員和編譯器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器