亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

編寫輕量ajax組件03-實現(附源碼)

2019-11-14 13:48:48
字體:
來源:轉載
供稿:網友

前言

  通過前兩篇的介紹,我們知道要執行頁面對象的方法,核心就是反射,是從請求獲取參數并執行指定方法的過程。實際上這和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約定

  通?,F在主流瀏覽器在使用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 = "";    }
View Code

  意思是如果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    }
View Code

各種處理程序和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)        {        }    }  
View Code

  異步狀態的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, HttpContext, CacheMethodInfo> action = new Action<Page, HttpContext, CacheMethodInfo>(Executor.Execute);            IAsyncResult result = action.BeginInvoke(_page, context, _cacheMethodInfo, cb, action);            return result;        }        public void EndProcessRequest(IAsyncResult result)        {            Action<Page, HttpContext, CacheMethodInfo> action = result.AsyncState as Action<Page, HttpContext, CacheMethodInfo>;            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)        {        }    }    
View Code

  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 name="page">處理頁面</param>        /// <param name="methodName">處理方法</param>        /// <param name="context">當前請求</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);        }    }
View Code

  上面的CacheMethodInfo是用于緩存調用方法的相關信息的,第一篇我們有提到過優化緩存的一些方法,其中就包括緩存+委托。但這里我們并不直接緩存方法的MethodInfo,因為緩存MethodInfo的話,需要通過Invoke去執行,這樣的效率比較低。這里我緩存的是方法的委托,該委托的簽名為:Func<object, object[], object>,該委托的返回值為object類型,表示可以返回任意的類型(我們可以在組件內部進行處理,例如如果是引用類型(非string),就將其序列化為json,但這里并沒有實現)。該委托接收兩個參數,第一個參數是方法所屬的對象,如果是靜態方法就是null;第二個參數是方法的參數,定義為object[]表示可以接收任意類型的參數。通過委托執行方法,與直接調用方法的效率差別就不是很大(對委托不熟悉的朋友可以參見:委托)。CacheMethodInfo的定義如下:

    /// <summary>    /// 緩存方法信息        /// </summary>    sealed class CacheMethodInfo    {        /// <summary>        /// 方法名稱        /// </summary>        public string MethodName { get; set; }        /// <summary>        /// 方法委托        /// </summary>        public Func<object, object[], object> Func { get; set; }        /// <summary>        /// 方法參數        /// </summary>        public ParameterInfo[] Parameters { get; set; }        /// <summary>        /// Ajax標記屬性        /// </summary>        public AjaxMethodAttribute AjaxMethodAttribute { get; set; }    }
View Code

核心方法

1. Eexcutor.GetDelegateInfo 獲取方法相關信息

  該方法用于遍歷頁面類,獲取所有AjaxMethodAttribute標記的方法信息,生成一個CacheMethodInfo對象,包括標記信息、方法名稱、參數信息,以及最重要的方法委托。該對象會緩存在一個哈希表中,下次獲取時,直接從內存獲得。

        /// <summary>        /// 獲取頁面標記方法信息        /// </summary>        /// <param name="page">頁面對象</param>        /// <param name="methodName">方法名稱</param>        internal static CacheMethodInfo GetDelegateInfo(Page page, string methodName)        {            if (page == null)            {                throw new ArgumentNullException("page");            }            Type type = page.GetType();            //ajaxDelegateTable是一個Hashtable            Dictionary<string, CacheMethodInfo> dic = ajaxDelegateTable[type.AssemblyQualifiedName] as Dictionary<string, CacheMethodInfo>;            if (dic == null)            {                dic = new Dictionary<string, CacheMethodInfo>();                //遍歷頁面的所有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 < length; i++)                {                                        CacheMethodInfo cacheMethodInfo = infos.ElementAt(i);                                        string name = cacheMethodInfo.MethodName;                    MethodInfo methodInfo = type.GetMethod(name);                    if (!dic.ContainsKey(name))                    {                                                //根據MethodInfo獲取方法委托                        cacheMethodInfo.Func = ReflectionUtil.GetMethodDelegate(methodInfo);                        dic.Add(name, cacheMethodInfo);                    }                }                ajaxDelegateTable[type.AssemblyQualifiedName] = dic;            }            CacheMethodInfo currentMethodInfo = null;            dic.TryGetValue(methodName, out currentMethodInfo);            return currentMethodInfo;                    }
View Code

  獲取方法的委托的是通過一個ReflectionUtil獲得的,該類主要用來優化反射,它通過Expression,可以將MethodInfo編譯成Func<object,object[],object>委托,為Type編譯一個Func<object>委托,用于創建實例對象。

  通過Expression優化反射

  Expression(表達式樹)允許我們將代碼邏輯以表達式的形式存儲在樹狀結構里,然后在運行時去動態解析,實現動態編輯和執行代碼。熟悉ORM框架的朋友對Expression肯定很熟悉,因為大部分方法都有一個Expression<TDelegate>類型的參數。訪問關系型數據庫的本質還是sql語句,orm的工作就是為開發人員屏蔽這個過程,以面向對象的方式去讀寫數據庫,而不是自己編寫sql語句。例如,Users.Where(u => u.Age > 18) 就可查詢年齡大于18的用戶。這里不對應用在orm的過程進行詳解,下面我們介紹如何用Expression并利用它來生成委托。

  .net定義了許多表達式類型,這些類型都派生自Expression,Expression是一個抽象類,而且是一個工廠類,所有類型的表達式都通過它來創建。如圖:

  先看一個 1 * 2 + 2 例子,我們用表達樹來描述來描述它:

            /*             *  a * b + 2              */            /*            直接操作            int a = 1, b = 2;            int result = a * 2 + 2;            */            /*            通過委托調用            Func<int, int, int> func = new Func<int, int, 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<int, int, int>>(unaryExpression, parametersExpression);            //將委托編譯成可執行代碼            Func<int,int,int> func = lambdaExpression.Compile() as Func<int,int,int>;            Console.WriteLine(func(1, 2));
View Code

  可以看到我們最終將其編譯為一個具體類型的委托了。下面看我們真正用到的方法是如何實現的,代碼如下:

        public static Func<object, object[], 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 < length; i++)            {                //獲取paramExp參數數組的第i個元素                BinaryExpression valueObj = Expression.ArrayIndex(paramExp, Expression.Constant(i));                //將其轉換為與參數類型一致的類型                UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);                //添加到參數集合                paramExpList.Add(valueCast);            }                 //方法所屬的實例的表達式,如果為靜態則為null            UnaryExpression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceExp, methodInfo.ReflectedType);            //表示調用方法的表達式             MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, paramExpList);            //將表達式目錄描述的lambda編譯為可執行代碼(委托)            if (methodCall.Type == typeof(void))            {                Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceExp, paramExp);                Action<object, object[]> action = lambda.Compile();                return (instance, parameters) =>                {                    action(instance, parameters);                    return null;                };            }            else            {                UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));                Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceExp, paramExp);                return lambda.Compile();            }        }
View Code

  具體代碼都有注釋解釋,最終我們獲得了一個Func<object,object[],object>類型的委托,它會作為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; }        }    }
View Code

  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; }        }    }
View Code

  定義一個DataMemberManager,用來遍歷Type,獲取所有字段和屬性的,實現如下:

    internal static class DataMemberManager    {        /// <summary>        /// 獲取實例字段/屬性集合        /// </summary>        /// <param name="type">類型</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;        }    }
View Code

  在前面我們定義的Handler的ProcessRequest方法中,我們調用了Executor.Execute,該方法用于執行委托,實現如下:

        /// <summary>        /// 核心函數,執行Handler的方法        /// </summary>        /// <param name="page">頁面對象</param>        /// <param name="context">請求上下文</param>        /// <param name="cacheMethodInfo">緩存方法原數據</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);            }        }
View Code

  CacheMethodInfo我們已經獲得了,現在只要獲得參數我們就可以執行方法。

  GetParameterFromRequest用于從請求獲取object[]參數數組。根據上面所說的,如果參數是一個簡單類型,那么直接進行轉換;如果是實例對象,那么我們先要創建new一個實例對象,然后為其字段或屬性賦值。實現如下:

        /// <summary>        /// 從請求獲取參參數        /// </summary>        /// <param name="request">HttpRequest</param>        ///<param name="parameters">參數信息</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 < length; i++)            {                ParameterInfo pi = parameters[i];                Type piType = pi.ParameterType.GetRealType();                object value = null;                if (piType.IsValueType())                {                    //值類型                    value = ModelUtil.GetValue(request, pi.Name, piType);                    value = value ?? Activator.CreateInstance(piType);                }                else if (piType.IsClass)                {                    //引用類型                    object model = ModelUtil.CreateModel(piType);                    ModelUtil.FillModelByRequest(request, pi.Name, piType, model);                    value = model;                }                else                {                    throw new NotSupportedException(pi.Name + " 參數不被支持");                }                realParameters[i] = value;            }            return realParameters;        }
View Code

  ModelUtil會從Http Request獲取參數,并進行類型轉換處理:

    internal static class ModelUtil    {        /// <summary>        /// 緩存構造函數        /// </summary>        private static Hashtable constructorTable = Hashtable.Synchronized(new Hashtable());         /// <summary>        /// 根據名稱從HttpRequest獲取值        /// </summary>        /// <param name="request">HttpRequest</param>        /// <param name="name">鍵名稱</param>        /// <param name="type">參數類型</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 name="type">實例類型</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 name="request">HttpRequest</param>        /// <param name="name">鍵名稱</param>        /// <param name="prefix">參數類型</param>        /// <parparam name="model">實例對象</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);            }        }    }
View Code

  如果是引用類型,需要通過構造函數創建對象,像前面用于,這里我們也用Expression來構建一個Func<object>類型的委托來優化,它調用了ReflectionUtil.GetConstructorDelegate方法。實現如下:

        /// <summary>        /// 獲取構造函數委托        /// </summary>        /// <param name="type">實例類型</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<object>>  lambda = Expression.Lambda<Func<object>>(newExp);            return lambda.Compile();        }
View Code
  最后再輸出結果時,如果是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();        }
View Code
總結
  現在不管我們前臺用什么腳本庫,只要按照約定就可以調用標記方法。上面已經介紹了組件的核心部分,您也可以按照自己的想法進行擴展,也歡迎共同學習交流。
  源碼下載

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品一区二区久久国产| 欧美日韩精品中文字幕| 国产精品99免视看9| 欧美成年人视频| 久久久这里只有精品视频| 青草青草久热精品视频在线观看| 国产精品都在这里| 亚洲人av在线影院| 国产精品wwwwww| 欧美成年人视频| 成人精品视频99在线观看免费| 亚洲精品之草原avav久久| 国产欧美婷婷中文| 成人欧美一区二区三区在线湿哒哒| 欧美午夜激情在线| 在线视频一区二区| 日韩精品在线视频观看| 欧美电影在线观看完整版| 久久艹在线视频| 欧美猛交免费看| 久久久国产影院| 狠狠色香婷婷久久亚洲精品| 亚洲老头老太hd| 国产精品激情自拍| 国产欧亚日韩视频| xvideos国产精品| 欧美精品一区二区免费| 久久久久久国产精品三级玉女聊斋| 久久婷婷国产麻豆91天堂| 亚洲国产精品系列| 欧美成人黑人xx视频免费观看| 不卡av电影在线观看| 亚洲午夜女主播在线直播| 亚洲精品资源美女情侣酒店| 国产精品露脸av在线| 中文字幕亚洲精品| 日本亚洲精品在线观看| 精品国产依人香蕉在线精品| 欧美日韩免费网站| 亚洲аv电影天堂网| 91免费看国产| 久久久国产视频91| 亚洲综合色av| 性色av一区二区咪爱| 国产精品一区二区在线| 色999日韩欧美国产| 亚洲人成电影网站色…| 亚洲va久久久噜噜噜| 91久久精品视频| 国产精品96久久久久久| 97人人爽人人喊人人模波多| 日韩美女中文字幕| 欧美高清视频在线观看| 亚洲人成网站777色婷婷| 米奇精品一区二区三区在线观看| 欧美精品福利在线| 日韩国产中文字幕| 国产精品尤物福利片在线观看| 亚洲一区二区久久久久久久| 久久99久国产精品黄毛片入口| 97视频在线观看播放| 久久精品最新地址| 久久久久久久久久久91| 亚洲男人7777| 久久电影一区二区| 欧美情侣性视频| 欧美人与性动交a欧美精品| 亚洲精品电影网在线观看| 成人xvideos免费视频| 欧美午夜片欧美片在线观看| 在线观看国产欧美| www.精品av.com| 久久亚洲综合国产精品99麻豆精品福利| 中文字幕久热精品在线视频| 成人在线视频网站| 国产日韩在线免费| 日韩在线精品视频| 久久国产精品首页| 韩国v欧美v日本v亚洲| 中文字幕欧美精品在线| 亚洲精品一区二三区不卡| 亚洲人成五月天| 日韩免费黄色av| 色综合色综合网色综合| 国产精品9999| 中国日韩欧美久久久久久久久| 亚洲精品国产电影| 国产精品盗摄久久久| 亚洲精品av在线播放| 国产精品美乳一区二区免费| 亚洲在线第一页| 国产精品丝袜久久久久久高清| 中文字幕免费国产精品| 国产视频欧美视频| 国产精品电影久久久久电影网| 久久久久久久久中文字幕| 爱福利视频一区| 欧美成人sm免费视频| 日韩欧美亚洲国产一区| 国产成人一区三区| 另类少妇人与禽zozz0性伦| 欧美大学生性色视频| 91精品视频在线看| 成人免费午夜电影| 日韩精品在线免费观看| 久久久电影免费观看完整版| 在线午夜精品自拍| 欧美老女人xx| 国产日韩欧美综合| 国产精品高潮呻吟久久av野狼| 亚洲精品一区二区网址| 国产精品91久久| 欧美国产亚洲视频| 国内精品视频一区| 欧美俄罗斯性视频| 日韩视频一区在线| 色婷婷亚洲mv天堂mv在影片| 亚洲男女自偷自拍图片另类| 8x海外华人永久免费日韩内陆视频| 久久久精品一区二区| 国产日韩欧美电影在线观看| 91最新国产视频| 97免费在线视频| 欧美精品在线观看91| 亚洲va码欧洲m码| 在线亚洲午夜片av大片| 国产一区二区三区毛片| 97色在线播放视频| 日韩成人高清在线| 久久视频免费观看| 成人黄色免费片| 欧美中文字幕在线播放| 欧美精品18videos性欧美| 美女av一区二区三区| 亚洲人成免费电影| 久久精品中文字幕一区| 91国内揄拍国内精品对白| 亚洲国产精品字幕| 欧美性极品xxxx娇小| 欧美激情中文字幕在线| 日韩欧美国产免费播放| 秋霞午夜一区二区| 国产精品亚洲аv天堂网| 欧美性xxxx极品hd满灌| 国产一级揄自揄精品视频| y97精品国产97久久久久久| 国产在线精品一区免费香蕉| 国产精品第100页| 黄色一区二区在线观看| 国产成人精品国内自产拍免费看| 97在线观看视频国产| 一夜七次郎国产精品亚洲| 日韩免费观看av| 欧美精品18videos性欧美| 91久久久在线| 中日韩美女免费视频网址在线观看| 欧美成人精品在线观看| 91网在线免费观看| 国产亚洲日本欧美韩国| 7m第一福利500精品视频| 国产精品尤物福利片在线观看| 日韩视频在线免费观看| 欧美夜福利tv在线| 亚洲精品久久久久中文字幕二区|