ASP.NET WebForm也可以这样用Ajax(程序猿,你知道不)

对于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框架。先来看这个框架的实现机制:
ASP.NET WebForm也可以这样用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页面中创建一个被调用的测试方法:
1 public List<string>  TestAjaxMethod(int a, string b, float c)  
2         {  
3                return new List<string> { a.ToString(), b, c.ToString() };  
4         } 
4.      在Default.aspx中写一个Ajax请求
5 PowerAjax.AsyncAjax(‘TestAjaxMethod’, [1, 2, “333”,”sss”], function (SucceessResponse) {  
6         // 成功后的代码  
7 }); 
PowerAjax.js是用Jquery专门为这个框架封装的一个及其简单的JS类库,这个类库中有两个主要的方法:PowerAjax.AsyncAjax和PowerAjax.SyncAjax,一个提供同步操作,一个提供异步操作,参数有三个:
第一个参数是即将操作在aspx.cs的Ajax方法的名字(用名字反射方法)。
第二个参数是一个以数组形式组成参数列表数据。
第三个参数是操作成功之后执行执行的回调方法,与c#中的委托一个道理。
以下为这个简单JS库的代码:
8 var PowerAjax = function () { }  
9 PowerAjax.__Private = function () { }  
10    
11 // 进行异步操作  
12 PowerAjax.AsyncAjax = function (methodName, paramArray, success) {  
13     PowerAjax.__Private.Ajax(methodName, paramArray, success, true);  
14 }  
15    
16 // 进行的是同步操作  
17 PowerAjax.SyncAjax = function (methodName, paramArray, success) {  
18     PowerAjax.__Private.Ajax(methodName, paramArray, success, false);  
19 }  
20    
21 PowerAjax.__Private.Ajax = function (methodName, paramArray, success, isAsync) {  
22     var data = {};  
23     switch (paramArray.length) {  
24         case 0:  
25             data = { ‘isAjaxRequest’: true, ‘MethodName’: methodName };  
26             break;  
27         case 1:  
28             data = { ‘isAjaxRequest’: true, ‘MethodName’: methodName, “param0”: paramArray[0] };  
29             break;  
30         case 2:  
31             data = { ‘isAjaxRequest’: true, ‘MethodName’: methodName, “param0”: paramArray[0], “param1”: paramArray[1] };  
32             break;  
33         case 3:  
34             data = { ‘isAjaxRequest’: true, ‘MethodName’: methodName, “param0”: paramArray[0], “param1”: paramArray[1], “param2”: paramArray[2] };  
35             break;  
36         case 4:  
37             data = { ‘isAjaxRequest’: true, ‘MethodName’: methodName, “param0”: paramArray[0], “param1”: paramArray[1], “param2”: paramArray[2], “param3”: paramArray[3] };  
38             break;  
39         case 5:  
40             data = { ‘isAjaxRequest’: true, ‘MethodName’: methodName, “param0”: paramArray[0], “param1”: paramArray[1], “param2”: paramArray[2], “param3”: paramArray[3], “param4”: paramArray[4] };  
41             break;  
42     }  
43    
44     var url = document.location.href;  
45     $.ajax({  
46         type: “post”,  
47         url: url,  
48         data: data,  
49         async: isAsync,  
50         datatype: “json”,  
51         contentType: “application/x-www-form-urlencoded; charset=UTF-8”,  
52         success: function (response) {  
53             success(response);  
54         },  
55         error: function (response) {  
56             if (response.status == 500) {  
57                 var errorMessage = response.responseText;  
58                 var errorTitle = errorMessage.substring(errorMessage.indexOf(“<title>”) + 7, errorMessage.indexOf(“</title>”))  
59                 throw new Error(“服务器内部错误:” + errorTitle);  
60             }  
61         }  
62     });  
63 } 
5. 更改Default.aspx.cs的继承页面为AjaxBasePage              
64 public partial class _Default : AjaxBasePage 
6. 主要基类:AjaxBasePage类
如下代码:
65 public class AjaxBasePage : System.Web.UI.Page  
66 {  
67     /// <summary>  
68     /// 是否是一个ajax请求。  
69     /// </summary>  
70     public bool IsAjaxRequest { get; private set; }  
71  
72     /// <summary>  
73     ///  如果是Ajax请求,劫持页面生命周期的PreInit的事件,直接返回Response  
74     /// </summary>  
75     protected override void OnPreInit(EventArgs e)  
76     {  
77         AjaxRequest ajaxRequest = AjaxRequest.GetInstance(Request.Form);  
78         this.IsAjaxRequest = ajaxRequest.IsAjaxRequest;  
79  
80         if (this.IsAjaxRequest)  
81         {  
82             AjaxApplication ajaxApplication = new AjaxApplication(this, ajaxRequest);  
83             ajaxApplication.EndRequest();  
84         }  
85         else 
86         {  
87             // 如果不是Ajax请求,继续执行页面生命周期的剩余事件  
88             base.OnPreInit(e);  
89         }  
90     }  
91 } 
该类重写了PreInit方法,判断请求是否是一个Ajax请求。通过AjaxRequest类接收并处理接收到的请求,提取出一些有效的数据,比如说是否是一个Ajax请求,方法的名字,参数列表(AjaxParameter类)。
至于AjaxParameter类,内部用的数据结构其实是一个字典,并使用索引来提供对数据的方便访问,提供一个Count属性,方便获取参数的个数。     如下代码
92 public class AjaxParameter  
93     {  
94         private IDictionary<int, string> m_DictionaryParamsData = new Dictionary<int, string>();  
95    
96         /// <summary>  
97         /// 返回参数的个数。  
98         /// </summary>  
99         public int Count  
100         {  
101             get  
102             {  
103                 return this.m_DictionaryParamsData.Count;  
104             }  
105         }  
106    
107         /// <summary>  
108         /// 索引具体参数的值。  
109         /// </summary>  
110         /// <param name=”index”></param>  
111         /// <returns></returns>  
112         public string this[int index]  
113         {  
114             get  
115             {  
116                 if (index >= 5 || index < 0)  
117                 {  
118                     throw new NotSupportedException(“请求方法的参数的个数限制为:0-5”);  
119                 }  
120                 return this.m_DictionaryParamsData[index];  
121             }  
122         }  
123    
124         public AjaxParameter(IDictionary<int, string> paramsData)  
125         {  
126             this.m_DictionaryParamsData = paramsData;  
127         }  
128     } 
AjaxRequest类的设计思路其实是模仿HttpContext设计,HttpContext能够从基础的http请求报文分析出以后处理将要用到的数据(response,request,session,cookie等等)数据,而AjaxRequest通过分析Ajax的Post请求的数据域 Data分析出各种以后会用到的数据。如下是该类的代码:
129 public class AjaxRequest  
130     {  
131         private Dictionary<int, string> m_DictionaryParamsData = new Dictionary<int, string>();  
132         private AjaxParameter m_AjaxParameter;  
133         private int m_Count = 0;  
134    
135         #region 属性  
136         /// <summary>  
137         /// 是否是一个Ajax请求。  
138         /// </summary>  
139         public bool IsAjaxRequest { get; private set; }  
140    
141         /// <summary>  
142         /// 请求的方法名字。  
143         /// </summary>  
144         public string MethodName { get; private set; }  
145    
146         /// <summary>  
147         /// 参数数据。  
148         /// </summary>  
149         public AjaxParameter Parameters  
150         {  
151             get { return this.m_AjaxParameter; }  
152         }  
153         #endregion  
154    
155         #region 构造函数  
156         private AjaxRequest(NameValueCollection nameValueCollection)  
157         {  
158             this.IsAjaxRequest = nameValueCollection[“isAjaxRequest”] == “true”;  
159             if (this.IsAjaxRequest)  
160             {  
161                 this.MethodName = nameValueCollection[“MethodName”];  
162    
163                 foreach (string value in nameValueCollection)  
164                 {  
165                     string formKey = string.Format(“param{0}”, this.m_Count);  
166                     if (nameValueCollection[formKey] != null)  
167                     {  
168                         this.m_DictionaryParamsData.Add(this.m_Count, nameValueCollection[formKey]);  
169                         this.m_Count++;  
170                     }  
171                 }  
172                 m_AjaxParameter = new AjaxParameter(this.m_DictionaryParamsData);  
173             }  
174         }  
175    
176         #endregion  
177    
178         #region 实例方法  
179         public static AjaxRequest GetInstance(NameValueCollection nameValueCollection)  
180         {  
181             return new AjaxRequest(nameValueCollection);  
182         }  
183         #endregion  
184    
185         #region ToString  
186         public override string ToString()  
187         {  
188             return this.MethodName;  
189         }  
190         #endregion  
191     } 
通过分析AjaxRequest的属性IsAjaxRequest可判断是否为Ajax请求,若该请求为一个Ajax请求,那么创建一个AjaxApplication实例,在创建AjaxApplication实例的过程中会利用当前页面和AjaxRequest提供的数据进行实际方法的调用(反射),该类是执行Ajax方法的核心类,其中会判断是否读取的到的方法是一个有效的方法,并判断从JS中AjaxApplication传入的参数类型的有效性,目前只提供对以下13中参数提供支持,如下:
192 (String、Boolean、Int32、Int64、UInt32、UInt64、Single、Double、Decimal、DateTime、DateTimeOffset、TimeSpan、Guid) 
如果方法中出现非以上类型,将会抛出异常。为了方便Ajax的调试,在JS前段类库中我会对异常进行处理,并抛出Error,Error信息有效的截取了继承自Exception的抛出信息,至于如何获    得更加详细的JS调试信息,以后JS库中可能会做提供更加详细的调用信息,毕竟框架是在改进中进行的。如下是AjaxApplication类的具体代码:
193 public class AjaxApplication  
194     {  
195         private AjaxBasePage m_AjaxBasePage;  
196         private object m_ResponseData;  
197    
198         public AjaxApplication(AjaxBasePage ajaxBasePage, AjaxRequest ajaxRequest)  
199         {  
200             this.m_AjaxBasePage = ajaxBasePage;  
201             Type ajaxBasePageType = ajaxBasePage.GetType();  
202             MethodInfo methodInfo = ajaxBasePageType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)  
203                                         .FirstOrDefault(item => item.Name == ajaxRequest.MethodName);  
204             object[] parameterData = this.GetParameterData(ajaxRequest, methodInfo);  
205    
206             if (methodInfo.IsStatic)  
207             {  
208                 this.m_ResponseData = methodInfo.Invoke(null, parameterData);  
209             }  
210             else 
211             {  
212                 this.m_ResponseData = methodInfo.Invoke(ajaxBasePage, parameterData);  
213             }  
214         }  
215    
216         /// <summary>  
217         /// 获取参数数据。  
218         /// </summary>  
219         private object[] GetParameterData(AjaxRequest ajaxRequest, MethodInfo methodInfo)  
220         {  
221             if (methodInfo != null)  
222             {  
223                 ParameterInfo[] parameterInfos = methodInfo.GetParameters();  
224    
225                 if (parameterInfos.Length > 5)  
226                 {  
227                     throw new NotSupportedException(“最多支持5个参数”);  
228                 }  
229    
230                 if (parameterInfos.Length > ajaxRequest.Parameters.Count)  
231                 {  
232                     throw new ArgumentException(“缺少参数!”);  
233                 }  
234    
235                 List<object> parameterData = new List<object>(parameterInfos.Length);  
236                 for (int i = 0; i < parameterInfos.Length; i++)  
237                 {  
238                     ParameterInfo parameterInfo = parameterInfos[i];  
239                     string paramValue = ajaxRequest.Parameters[i];  
240    
241                     try 
242                     {  
243                         parameterData.Add(ParseAjaxParameter(paramValue, parameterInfo));  
244                     }  
245                     catch (FormatException)  
246                     {  
247                         string format = string.Format(“传入静态方法 {0} 的第 {1} 个(从0开始计数)参数类型不匹配,应该为 {2} 类型 请检查!”, methodInfo.Name, i, parameterInfo.ParameterType.Name);  
248                         throw new FormatException(format);  
249                     }  
250                 }  
251                 return parameterData.ToArray();  
252             }  
253             else 
254             {  
255                 throw new InvalidOperationException(“没有发现此方法,请检查该方法签名(方法必须为public)”);  
256             }  
257         }  
258    
259         /// <summary>  
260         /// 类型转换。支持 String、Boolean、Int32、Int64、UInt32、UInt64、Single、Double、Decimal、DateTime、DateTimeOffset、TimeSpan、Guid  
261         /// </summary>  
262         private object ParseAjaxParameter(string ajaxParameterValue, ParameterInfo parameterInfo)  
263         {  
264             object obj;  
265             if (parameterInfo.ParameterType == typeof(String))  
266             {  
267                 obj = ajaxParameterValue;  
268             }  
269             else if (parameterInfo.ParameterType == typeof(Boolean))  
270             {  
271                 obj = bool.Parse(ajaxParameterValue);  
272             }  
273             else if (parameterInfo.ParameterType == typeof(Int32))  
274             {  
275                 obj = Int32.Parse(ajaxParameterValue);  
276             }  
277             else if (parameterInfo.ParameterType == typeof(UInt32))  
278             {  
279                 obj = UInt32.Parse(ajaxParameterValue);  
280             }  
281             else if (parameterInfo.ParameterType == typeof(UInt64))  
282             {  
283                 obj = UInt64.Parse(ajaxParameterValue);  
284             }  
285             else if (parameterInfo.ParameterType == typeof(Single))  
286             {  
287                 obj = Single.Parse(ajaxParameterValue);  
288             }  
289             else if (parameterInfo.ParameterType == typeof(Double))  
290             {  
291                 obj = Double.Parse(ajaxParameterValue);  
292             }  
293             else if (parameterInfo.ParameterType == typeof(Decimal))  
294             {  
295                 obj = Decimal.Parse(ajaxParameterValue);  
296             }  
297             else if (parameterInfo.ParameterType == typeof(DateTime))  
298             {  
299                 obj = DateTime.Parse(ajaxParameterValue);  
300             }  
301             else if (parameterInfo.ParameterType == typeof(DateTimeOffset))  
302             {  
303                 obj = DateTimeOffset.Parse(ajaxParameterValue);  
304             }  
305             else if (parameterInfo.ParameterType == typeof(TimeSpan))  
306             {  
307                 obj = TimeSpan.Parse(ajaxParameterValue);  
308             }  
309             else if (parameterInfo.ParameterType == typeof(Guid))  
310             {  
311                 obj = Guid.Parse(ajaxParameterValue);  
312             }  
313             else 
314             {  
315                 throw new NotSupportedException(“方法参数类型不支持!”);  
316             }  
317             return obj;  
318         }  
319    
320         /// <summary>  
321         /// 结束页面生命周期,同时直接执行应用程序生命周期的EndRequest事件。  
322         /// </summary>  
323         public void EndRequest()  
324         {  
325             HttpResponse response = this.m_AjaxBasePage.Page.Response;  
326             response.ContentType = “application/json”;  
327             response.Clear();  
328             JavaScriptSerializer jsonSerializer2 = new JavaScriptSerializer();  
329             response.Write(jsonSerializer2.Serialize(new JsonResponse { IsSuccess = true, Message = “处理成功”, ResponseData = this.m_ResponseData }));  
330             response.End();  
331         }  
332     } 
当初始化了一个AjaxApplication实例后, 可以调用该实例的EndRequest()方法,来结束Ajax请求。该方法内部最后调用Response.End()方法来结束页面生命周期和大部分管线事件。
并用JsonResponse类来封装返回数据。
333 public class JsonResponse  
334 {  
335     public bool IsSuccess { get; set; }  
336     public string Message { get; set; }  
337     public object ResponseData { get; set; }  
338 } 
该类最后一个参数即承载了调用方法的返回值,为一个Object类型,也就是说,框架可以支持任何类型的返回值,当然该类型可以被序列化。
7. 回过头来再看Ajax请求,针对返回值可以这样用:
339 PowerAjax.AsyncAjax(‘TestAjaxMethod’, [1, 2, “333”, “sss”], function (SucceessResponse) {  
340     if (SucceessResponse.IsSuceess) {  
341         alert(“恭喜,该ajax方法调用成功!”);  
342         Process(SucceessResponse.ResponseData); // 处理返回的数据,这里可能需要你自己实现了,因为我无法判断你要返回的是什么东西,这是个Object  
343     } else {  
344         alert(“这是操作错误奥,不是内部异常,内部异常的抛出会我内部会处理的!”);  
345         alert(“错误信息:” + SucceessResponse.Message);  
346     }  
347 });  
以上是试水的东西,希望各位大牛指正。
 

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/58284.html

(0)
上一篇 2021年8月9日
下一篇 2021年8月9日

相关推荐

发表回复

登录后才能评论