zoukankan      html  css  js  c++  java
  • asp.net webForm也可以这样用Ajax My Ajax Framework

           对于asp.net WebForm项目,进行Ajax操作大概有三种方式:web服务(.asmx文件)  ,  一般处理程序(.ashx)和  一些Ajax控件。

    对于.net提供的ajax控件,暂且不说,只说另外两种方式,都需要引入额外的代码文件对Ajax进行操作(asmx和ashx,且web服务还要引入一个cs文件与之对应),假如要对Example.aspx这个页面添加一些自定义的Ajax操作,并且这些Ajax操作并不会在别的页面上用到,如此不得不引入额外的代码文件完成这个操作,假如这个Ajax操作很简单,只需要一个简单的函数就可以执行,那岂不是很麻烦的过程吗?如此一来,随着项目中Ajax操作的增多,ashx和asmx文件都会随着时间的增长而增长,项目的维护也会随之加倍。为什么我们不能把Ajax操作的后台代码直接放在Example.aspx对应的Example.aspx.cs文件里 ? 如果能这样做,以上两种烦恼都会迎刃而解,不是吗?

          于是,按照以上思路,实现了如下Ajax框架。先来看这个框架的实现机制:

     

          上图是自己画的一个缩减版IIS接收到一个aspx请求的HttpApplication管线和asp.net Page在执行ProcessRequest()方法中的页面生命周期的部分事件。Page本身是继承自IHttpHandler接口,IHttpHandler提供了一个重要的约束方法ProcessRequest,该方法是对接收到的信息(HttpContext)进行具体的处理同样,一般处理程序和web服务也实现了自己的IHttpHandler,并以此提供一些Ajax服务。具体的操作过程请自行查找MSDN。

           原理是在页面生命周期开始的第一个事件PreInit进行一些处理,一旦发现劫持到的请求是一个ajax请求,那么利用C#的反射来调用aspx.cs中定义的方法,执行完方法之后,调用Response.End()方法,调用这个方法会直接跳到管线的EndRequest事件,从而结束请求,这样就无需走一些没有必要的页面生命周期的步骤,从而完成一个Ajax操作。如果发现是一个正常的操作,那么就走正常流程。

     

    下面以一个简单例子说明该Ajax框架的使用:

    1.       添加一个解决方案

    2.       新建一个 Default.aspx 页面

    3.       在Default.aspx.cs页面中创建一个被调用的测试方法:

              public List<string>  TestAjaxMethod(int a, string b, float c)
              {
                     return new List<string> { a.ToString(), b, c.ToString() };
              }

    4.      在Default.aspx中写一个Ajax请求

              PowerAjax.AsyncAjax(‘TestAjaxMethod’, [1, 2, "333","sss"], function (SucceessResponse) {
                      // 成功后的代码
              });

          PowerAjax.js是用Jquery专门为这个框架封装的一个及其简单的JS类库,这个类库中有两个主要的方法:PowerAjax.AsyncAjax和PowerAjax.SyncAjax,一个提供同步操作,一个    提供异步操作,参数有三个:

          第一个参数是即将操作在aspx.cs的Ajax方法的名字(用名字反射方法)。

          第二个参数是一个以数组形式组成参数列表数据。

          第三个参数是操作成功之后执行执行的回调方法,与c#中的委托一个道理。

          以下为这个简单JS库的代码:

    var PowerAjax = function () { }
    PowerAjax.__Private = function () { }
    
    // 进行异步操作
    PowerAjax.AsyncAjax = function (methodName, paramArray, success) {
        PowerAjax.__Private.Ajax(methodName, paramArray, success, true);
    }
    
    // 进行的是同步操作
    PowerAjax.SyncAjax = function (methodName, paramArray, success) {
        PowerAjax.__Private.Ajax(methodName, paramArray, success, false);
    }
    
    PowerAjax.__Private.Ajax = function (methodName, paramArray, success, isAsync) {
        var data = {};
        switch (paramArray.length) {
            case 0:
                data = { 'isAjaxRequest': true, 'MethodName': methodName };
                break;
            case 1:
                data = { 'isAjaxRequest': true, 'MethodName': methodName, "param0": paramArray[0] };
                break;
            case 2:
                data = { 'isAjaxRequest': true, 'MethodName': methodName, "param0": paramArray[0], "param1": paramArray[1] };
                break;
            case 3:
                data = { 'isAjaxRequest': true, 'MethodName': methodName, "param0": paramArray[0], "param1": paramArray[1], "param2": paramArray[2] };
                break;
            case 4:
                data = { 'isAjaxRequest': true, 'MethodName': methodName, "param0": paramArray[0], "param1": paramArray[1], "param2": paramArray[2], "param3": paramArray[3] };
                break;
            case 5:
                data = { 'isAjaxRequest': true, 'MethodName': methodName, "param0": paramArray[0], "param1": paramArray[1], "param2": paramArray[2], "param3": paramArray[3], "param4": paramArray[4] };
                break;
        }
    
        var url = document.location.href;
        $.ajax({
            type: "post",
            url: url,
            data: data,
            async: isAsync,
            datatype: "json",
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
            success: function (response) {
                success(response);
            },
            error: function (response) {
                if (response.status == 500) {
                    var errorMessage = response.responseText;
                    var errorTitle = errorMessage.substring(errorMessage.indexOf("<title>") + 7, errorMessage.indexOf("</title>"))
                    throw new Error("服务器内部错误:" + errorTitle);
                }
            }
        });
    }
    

      

    5.       更改Default.aspx.cs的继承页面为AjaxBasePage              

    public partial class _Default : AjaxBasePage

    6.        主要基类:AjaxBasePage

    如下代码:

    public class AjaxBasePage : System.Web.UI.Page
    {
        /// <summary>
        /// 是否是一个ajax请求。
        /// </summary>
        public bool IsAjaxRequest { get; private set; }
    
        /// <summary>
        ///  如果是Ajax请求,劫持页面生命周期的PreInit的事件,直接返回Response
        /// </summary>
        protected override void OnPreInit(EventArgs e)
        {
            AjaxRequest ajaxRequest = AjaxRequest.GetInstance(Request.Form);
            this.IsAjaxRequest = ajaxRequest.IsAjaxRequest;
    
            if (this.IsAjaxRequest)
            {
                AjaxApplication ajaxApplication = new AjaxApplication(this, ajaxRequest);
                ajaxApplication.EndRequest();
            }
            else
            {
                // 如果不是Ajax请求,继续执行页面生命周期的剩余事件
                base.OnPreInit(e);
            }
        }
    }

            该类重写了PreInit方法,判断请求是否是一个Ajax请求。通过AjaxRequest类接收并处理接收到的请求,提取出一些有效的数据,比如说是否是一个Ajax请求,方法的名字,参数列表(AjaxParameter类)。

            至于AjaxParameter类,内部用的数据结构其实是一个字典,并使用索引来提供对数据的方便访问,提供一个Count属性,方便获取参数的个数。     如下代码:

    public class AjaxParameter
        {
            private IDictionary<int, string> m_DictionaryParamsData = new Dictionary<int, string>();
    
            /// <summary>
            /// 返回参数的个数。
            /// </summary>
            public int Count
            {
                get
                {
                    return this.m_DictionaryParamsData.Count;
                }
            }
    
            /// <summary>
            /// 索引具体参数的值。
            /// </summary>
            /// <param name="index"></param>
            /// <returns></returns>
            public string this[int index]
            {
                get
                {
                    if (index >= 5 || index < 0)
                    {
                        throw new NotSupportedException("请求方法的参数的个数限制为:0-5");
                    }
                    return this.m_DictionaryParamsData[index];
                }
            }
    
            public AjaxParameter(IDictionary<int, string> paramsData)
            {
                this.m_DictionaryParamsData = paramsData;
            }
        }
    

      

             AjaxRequest类的设计思路其实是模仿HttpContext设计,HttpContext能够从基础的http请求报文分析出以后处理将要用到的数据(response,request,session,cookie等等)数据,而AjaxRequest通过分析Ajax的Post请求的数据域Data分析出各种以后会用到的数据。如下是该类的代码:

    public class AjaxRequest
        {
            private Dictionary<int, string> m_DictionaryParamsData = new Dictionary<int, string>();
            private AjaxParameter m_AjaxParameter;
            private int m_Count = 0;
    
            #region 属性
            /// <summary>
            /// 是否是一个Ajax请求。
            /// </summary>
            public bool IsAjaxRequest { get; private set; }
    
            /// <summary>
            /// 请求的方法名字。
            /// </summary>
            public string MethodName { get; private set; }
    
            /// <summary>
            /// 参数数据。
            /// </summary>
            public AjaxParameter Parameters
            {
                get { return this.m_AjaxParameter; }
            }
            #endregion
    
            #region 构造函数
            private AjaxRequest(NameValueCollection nameValueCollection)
            {
                this.IsAjaxRequest = nameValueCollection["isAjaxRequest"] == "true";
                if (this.IsAjaxRequest)
                {
                    this.MethodName = nameValueCollection["MethodName"];
    
                    foreach (string value in nameValueCollection)
                    {
                        string formKey = string.Format("param{0}", this.m_Count);
                        if (nameValueCollection[formKey] != null)
                        {
                            this.m_DictionaryParamsData.Add(this.m_Count, nameValueCollection[formKey]);
                            this.m_Count++;
                        }
                    }
                    m_AjaxParameter = new AjaxParameter(this.m_DictionaryParamsData);
                }
            }
    
            #endregion
    
            #region 实例方法
            public static AjaxRequest GetInstance(NameValueCollection nameValueCollection)
            {
                return new AjaxRequest(nameValueCollection);
            }
            #endregion
    
            #region ToString
            public override string ToString()
            {
                return this.MethodName;
            }
            #endregion
        }

            通过分析AjaxRequest的属性IsAjaxRequest可判断是否为Ajax请求,若该请求为一个Ajax请求,那么创建一个AjaxApplication实例,在创建AjaxApplication实例的过程中会利用当前页面和AjaxRequest提供的数据进行实际方法的调用(反射),该类是执行Ajax方法的核心类,其中会判断是否读取的到的方法是一个有效的方法,并判断从JS中AjaxApplication传入的参数类型的有效性,目前只提供对以下13中参数提供支持,如下:

    (String、Boolean、Int32、Int64、UInt32、UInt64、Single、Double、Decimal、DateTime、DateTimeOffset、TimeSpan、Guid)

             如果方法中出现非以上类型,将会抛出异常。为了方便Ajax的调试,在JS前段类库中我会对异常进行处理,并抛出Error,Error信息有效的截取了继承自Exception的抛出信息,至于如何获    得更加详细的JS调试信息,以后JS库中可能会做提供更加详细的调用信息,毕竟框架是在改进中进行的。如下是AjaxApplication类的具体代码:

    public class AjaxApplication
        {
            private AjaxBasePage m_AjaxBasePage;
            private object m_ResponseData;
    
            public AjaxApplication(AjaxBasePage ajaxBasePage, AjaxRequest ajaxRequest)
            {
                this.m_AjaxBasePage = ajaxBasePage;
                Type ajaxBasePageType = ajaxBasePage.GetType();
                MethodInfo methodInfo = ajaxBasePageType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
                                            .FirstOrDefault(item => item.Name == ajaxRequest.MethodName);
                object[] parameterData = this.GetParameterData(ajaxRequest, methodInfo);
    
                if (methodInfo.IsStatic)
                {
                    this.m_ResponseData = methodInfo.Invoke(null, parameterData);
                }
                else
                {
                    this.m_ResponseData = methodInfo.Invoke(ajaxBasePage, parameterData);
                }
            }
    
            /// <summary>
            /// 获取参数数据。
            /// </summary>
            private object[] GetParameterData(AjaxRequest ajaxRequest, MethodInfo methodInfo)
            {
                if (methodInfo != null)
                {
                    ParameterInfo[] parameterInfos = methodInfo.GetParameters();
    
                    if (parameterInfos.Length > 5)
                    {
                        throw new NotSupportedException("最多支持5个参数");
                    }
    
                    if (parameterInfos.Length > ajaxRequest.Parameters.Count)
                    {
                        throw new ArgumentException("缺少参数!");
                    }
    
                    List<object> parameterData = new List<object>(parameterInfos.Length);
                    for (int i = 0; i < parameterInfos.Length; i++)
                    {
                        ParameterInfo parameterInfo = parameterInfos[i];
                        string paramValue = ajaxRequest.Parameters[i];
    
                        try
                        {
                            parameterData.Add(ParseAjaxParameter(paramValue, parameterInfo));
                        }
                        catch (FormatException)
                        {
                            string format = string.Format("传入静态方法 {0} 的第 {1} 个(从0开始计数)参数类型不匹配,应该为 {2} 类型 请检查!", methodInfo.Name, i, parameterInfo.ParameterType.Name);
                            throw new FormatException(format);
                        }
                    }
                    return parameterData.ToArray();
                }
                else
                {
                    throw new InvalidOperationException("没有发现此方法,请检查该方法签名(方法必须为public)");
                }
            }
    
            /// <summary>
            /// 类型转换。支持 String、Boolean、Int32、Int64、UInt32、UInt64、Single、Double、Decimal、DateTime、DateTimeOffset、TimeSpan、Guid
            /// </summary>
            private object ParseAjaxParameter(string ajaxParameterValue, ParameterInfo parameterInfo)
            {
                object obj;
                if (parameterInfo.ParameterType == typeof(String))
                {
                    obj = ajaxParameterValue;
                }
                else if (parameterInfo.ParameterType == typeof(Boolean))
                {
                    obj = bool.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(Int32))
                {
                    obj = Int32.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(UInt32))
                {
                    obj = UInt32.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(UInt64))
                {
                    obj = UInt64.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(Single))
                {
                    obj = Single.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(Double))
                {
                    obj = Double.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(Decimal))
                {
                    obj = Decimal.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(DateTime))
                {
                    obj = DateTime.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(DateTimeOffset))
                {
                    obj = DateTimeOffset.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(TimeSpan))
                {
                    obj = TimeSpan.Parse(ajaxParameterValue);
                }
                else if (parameterInfo.ParameterType == typeof(Guid))
                {
                    obj = Guid.Parse(ajaxParameterValue);
                }
                else
                {
                    throw new NotSupportedException("方法参数类型不支持!");
                }
                return obj;
            }
    
            /// <summary>
            /// 结束页面生命周期,同时直接执行应用程序生命周期的EndRequest事件。
            /// </summary>
            public void EndRequest()
            {
                HttpResponse response = this.m_AjaxBasePage.Page.Response;
                response.ContentType = "application/json";
                response.Clear();
                JavaScriptSerializer jsonSerializer2 = new JavaScriptSerializer();
                response.Write(jsonSerializer2.Serialize(new JsonResponse { IsSuccess = true, Message = "处理成功", ResponseData = this.m_ResponseData }));
                response.End();
            }
        }
    

      

            当初始化了一个AjaxApplication实例后, 可以调用该实例的EndRequest()方法,来结束Ajax请求。该方法内部最后调用Response.End()方法来结束页面生命周期和大部分管线事件。

    并用JsonResponse类来封装返回数据。

    public class JsonResponse
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public object ResponseData { get; set; }
    }

             该类最后一个参数即承载了调用方法的返回值,为一个Object类型,也就是说,框架可以支持任何类型的返回值,当然该类型可以被序列化。

    7.        回过头来再看Ajax请求,针对返回值可以这样用:

                    PowerAjax.AsyncAjax('TestAjaxMethod', [1, 2, "333", "sss"], function (SucceessResponse) {
                        if (SucceessResponse.IsSuceess) {
                            alert("恭喜,该ajax方法调用成功!");
                            Process(SucceessResponse.ResponseData); // 处理返回的数据,这里可能需要你自己实现了,因为我无法判断你要返回的是什么东西,这是个Object
                        } else {
                            alert("这是操作错误奥,不是内部异常,内部异常的抛出会我内部会处理的!");
                            alert("错误信息:" + SucceessResponse.Message);
                        }
                    });
                });

    以上是试水的东西,希望各位大牛指正。

  • 相关阅读:
    HDU 3401 Trade
    POJ 1151 Atlantis
    HDU 3415 Max Sum of MaxKsubsequence
    HDU 4234 Moving Points
    HDU 4258 Covered Walkway
    HDU 4391 Paint The Wall
    HDU 1199 Color the Ball
    HDU 4374 One hundred layer
    HDU 3507 Print Article
    GCC特性之__init修饰解析 kasalyn的专栏 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/A_ming/p/2986949.html
Copyright © 2011-2022 走看看