C#实现简易ajax调用后台方法
在当前WEB当中,有些人都会抛弃asp.net的服务器控件,转而使用ajax来进行数据的交互和存储.
当我们大量使用ajax的时候,对于新手而言,肯定会创建很多的ashx或aspx页面,通过拼接参数,在ashx或aspx中对参数进行解析,
并根据某些特定的参数进行解析来判断当前的请求属于哪种类型的操作,然后手动获取Request.QueryString、Params、Form内的值,来实现功能.大致代码如下:
1 //html 2 名字:<input id="name" type="text"/> 3 <input type="button" value="查询" onclick="search()"/> 4 <script type="text/javascript"> 5 function search(){ 6 var name = $('#name').val(); 7 //省略验证过程 8 $.ajax({ 9 url: 'Customer.ashx', 10 data: { type: 'search', name : name }, 11 dataType: 'json', 12 success: function(json){ 13 //对于获取的数据执行相关的操作,如:绑定、显示等 14 } 15 }); 16 }; 17 </script> 18 //ashx 19 public void ProcessRequest(HttpContext context) 20 { 21 var type = context.Request.Params["type"]; 22 if(type == "search") 23 { 24 var name = context.Request.Params["name"]; 25 //获取数据,并转化为json 26 var json = ...//省略 27 context.Response.Write(json); 28 } 29 }
在大量的重复这些编写这些代码的情况下,众多的新手们肯定都会跟我一样,蛋疼啊,而且随着业务及模块的不断增加,这些个ashx或aspx,那个量还不是一般的多呢.
而且每个都只是做一些判断,根本没什么重要的业务在里面,于是乎我们就在想,有没有什么方法可以让我们在ajax内传递某些参数,达到直接反射类,访问对应的方法,自动处理参数等.
根据以上的一些要求,的确可以让我们在编码过程当中,减少很多的困惑和麻烦.那我们现在就一步步开始吧.
首先,我们先从ajax开始,传递某些特定的值来反射对应的类,那么我们的第一反应就是将类的整个完整命名传递过去(因为我们并不能保证所有要调用的类都在同一个程序集内).
那么我们就在js内定义2个参数分别为assembly和fullName吧.我们假设Customer业务类的FullName为SysBLL.CustomerBLL且所在的程序集为SysBLL.
那么前面的例子ajax的修改大致如下:
1 <script type="text/javascript"> 2 function search(){ 3 var name = $('#name').val(); 4 //省略验证过程 5 $.ajax({ 6 url: 'BLLAshx.ashx', 7 data: { assemlby : 'SysBLL', fullName: 'SysBLL.CustomerBLL', method: 'Search', name : name }, 8 dataType: 'json', 9 success: function(json){ 10 //对于获取的数据执行相关的操作,如:绑定、显示等 11 } 12 }); 13 }; 14 </script>
从我们的需求上,我们了解到,我们只需要一个ashx来作为代理,去反射对应的类和方法,并从传递的参数中过滤出方法所需要的参数,最后调用方法取得返回值.代码大致如下:
1 View Code 2 //ashx 3 namespace Common 4 { 5 public class BLLAshx : IHttpHandler 6 { 7 public void ProcessRequest(HttpContext context) 8 { 9 string assemblyName = context.Request.QueryString["assemlby"], fullName = context.Request.Params["fullName"]; 10 var bllType = Assembly.Load(assemblyName).GetType(fullName); 11 12 var methodName = context.Request.Params["method"]; 13 var method = bllType.GetMethod(methodName); 14 if (null != method) 15 { 16 string[] parameterValues = GetMethodParameterValues(context.Request, method); 17 18 var instance = Activator.CreateInstance(bllType); 19 20 var result = method.ReturnType == typeof(void) ? "{}" : method.Invoke(instance, parameterValues).ToString(); 21 22 //以上Invoke省略了判断以及捕捉异常 23 context.Response.Write(result); 24 } 25 else 26 { 27 //返回不存在方法的提示 28 } 29 } 30 31 private string[] GetMethodParameterValues(HttpRequest request, MethodInfo method) 32 { 33 string[] parameterValues = null; 34 var methodParameters = method.GetParameters(); 35 if (0 < methodParameters.Length) 36 { 37 parameterValues = new string[methodParameters.Length]; 38 for (int i = 0; i < methodParameters.Length; i++) 39 { 40 parameterValues[i] = request.Params[methodParameters[i].Name]; 41 } 42 } 43 return parameterValues; 44 } 45 } 46 } 47 //CustomerBLL 48 namespace SysBLL 49 { 50 public class CustomerBLL 51 { 52 public string Search(string name) 53 { 54 var json = ...//获取数据 55 return json; 56 } 57 } 58 }
对于以上的ashx,我们需要在web项目中,新建一个名为BLLAshx的ashx文件,但是我们不要.cs的文件,并作如下修改:
<%@ WebHandler Language="C#" CodeBehind="BLLAshx.cs" Class="Common.BLLAshx" %>
现在我们再次回到ajax调用的地方,大家都会发现我们的参数列表室在太长了,因此大家可能会想用一些方法来改善,比如:连ashx都懒的重复去写了,想直接在url中把弥命名空间、类、方法直接放进去来替代,想到这一步的时候,我们就需要配置web.config了,假设我们想要的规则是SysBLL/SysBLL.CustomerBLL.Search,配置如下:
<httpHandlers> <add path="*/*.*.*.do" type="Common.BLLAshx" verb="GET,POST"/> </httpHandlers>
有人会问为什么这边要.do,而不直接省略呢?因为如果不加.do,那么就会将其他的请求也都转向我们的ashx,例如:js、css文件等,大家可以自己测试看看.因为我们使用了地址映射,因此我们的js以及ashx代码又要做一些改动了.大致修改如下:
1 View Code 2 //ashx 3 namespace Common 4 { 5 public class BLLAshx : IHttpHandler 6 { 7 public void ProcessRequest(HttpContext context) 8 { 9 string assemblyName = context.Request.QueryString["assemlby"], fullName = context.Request.Params["fullName"]; 10 var bllType = Assembly.Load(assemblyName).GetType(fullName); 11 12 var methodName = context.Request.Params["method"]; 13 var method = bllType.GetMethod(methodName); 14 if (null != method) 15 { 16 string[] parameterValues = GetMethodParameterValues(context.Request, method); 17 18 var instance = Activator.CreateInstance(bllType); 19 20 var result = method.ReturnType == typeof(void) ? "{}" : method.Invoke(instance, parameterValues).ToString(); 21 22 //以上Invoke省略了判断以及捕捉异常 23 context.Response.Write(result); 24 } 25 else 26 { 27 //返回不存在方法的提示 28 } 29 } 30 31 private string[] GetMethodParameterValues(HttpRequest request, MethodInfo method) 32 { 33 string[] parameterValues = null; 34 var methodParameters = method.GetParameters(); 35 if (0 < methodParameters.Length) 36 { 37 parameterValues = new string[methodParameters.Length]; 38 for (int i = 0; i < methodParameters.Length; i++) 39 { 40 parameterValues[i] = request.Params[methodParameters[i].Name]; 41 } 42 } 43 return parameterValues; 44 } 45 } 46 } 47 //CustomerBLL 48 namespace SysBLL 49 { 50 public class CustomerBLL 51 { 52 public string Search(string name) 53 { 54 var json = ...//获取数据 55 return json; 56 } 57 } 58 }
ashx的调整,我们只要修改ProcessRequest方法,代码大致如下:
1 var reg = new Regex(@"(?<assembly>\w+)\/(?<class>\w+\.\w+)\.(?<method>\w+)\."); 2 var groups = reg.Match(context.Request.AppRelativeCurrentExecutionFilePath).Groups; 3 if (4 == groups.Count) 4 { 5 string assemblyName = groups["assembly"].Value, fullName = groups["class"].Value; 6 var bllType = Assembly.Load(assemblyName).GetType(fullName); 7 8 var methodName = groups["method"].Value; 9 var method = bllType.GetMethod(methodName); 10 if (null != method) 11 { 12 string[] parameterValues = GetMethodParameterValues(context.Request, method); 13 14 var instance = Activator.CreateInstance(bllType); 15 16 var result = method.ReturnType == typeof(void) ? "{}" : method.Invoke(instance, parameterValues).ToString(); 17 //以上Invoke省略了判断以及捕捉异常 18 context.Response.Write(result); 19 } 20 else 21 { 22 //返回不存在方法的提示 23 } 24 } 25 else 26 { 27 //url匹配不正确 28 }
有人可能会问为什么要重复SysBLL/SysBLL呢?其实可以省略其中的一个,但是条件是程序集必须要跟这个前缀一样才能省略.
如果大家想要省略其中一次SysBLL,那么正则的匹配规则也要相应的调整一下,只要将(?<class>\w+.\w+)中的.\w+去掉即可.
大致的实现流程就到这里了,我们已经将大部分的需求基本完成了,还有一些遗留的问题留给大家去完善啦,比如:调用的方法参数必须都是string类型,方法的返回值也必须要是string类型,反射的类不能是泛型类型的等等.
因为以上的代码是随着文章编写的,因此没有示例代码,没有办法提供给大家,请见谅,呵呵.以上如有错误,请大家告诉我,多谢.
因为例子要简单易理解,所以编码方面不管是程序集、类名、方法名都是明码,可能让部分人有所错觉,在实际开发当中,可以使用类似交易码之类的来替代对应的程序集、类名、方法名,这样就不会那么透明了,至于js方面的安全性,大家可以看看google的Gmail,以上仅仅是个人的理解,有错误,也希望大家能提出,呵呵。