近期用asp.net做个小网站,但又不喜欢使用asp.net的服务器端控件,经过一番思量后确定前端采用原始的html、后台采用Linq to Sql与数据库交互,Linq上面创建一业务逻辑层,而web前端通过使用jQuery的ajax方法异步调用asp.net的一般处理程序(handler),实现与后台的业务逻辑层交互,至此大方向开发框架确立,只是细节上jQuery调用ashx有点小麻烦,一次只能调用一个ashx类,如此这般,一个模块增、删、改、查一套功能下来我不是得创建多个handler类?项目虽小,但这样做下来势必会产生很多ashx文件,对代码管理与阅读相当的不便,如果一个完整的功能模块下来只对应一个handler类该多好,通过调用同一个handler类执行不同方法来完成一模块中的不同的功能,就像java的strus框架的Action那样,经过思考隐约觉得使用反射机制可以实现上述想法,然后又百度了一下参考前人经验,经过实验确实可行,做了如下总结:
(1).使用反射机制的Invoke方法需要引用System.Reflection类。在使用jQuery的Ajax方法时,固定一个传值参数用来向handler类传送要执行的指定方法,参数可以随便命,但确定下来后就不能更改,我这里统一命名为"Action"。
(2).为节省劳动力与成本,避免重复编码我创建名为"BaseHandler.ashx"的基类,在这个类里边除了实现反射外,同时提供了一些通用方法以方便继承它的子类调用,所有的Handler类均继承该类。主要代码如下:

1 /// <summary> 2 /// 通过反射机制执行指定方法 3 /// </summary> 4 /// <param name="context"></param> 5 public void ProcessRequest(HttpContext context) 6 { 7 const string MESSAGE = "无法获取参数值或参数值不正确。"; 8 9 string strAction = ""; 10 Object[] objParameter; 11 Type[] objParamType; 12 Type objMethodType; 13 MethodInfo objMethod; 14 15 //获取参数传递(Acion为要执行的方法名) 16 strAction = GetRequest(context, "Action"); 17 if (strAction != "") 18 { 19 //获取调用类的类型 20 objMethodType = this.GetType(); 21 22 //设置方法参数值,并存入数组(在这里将HttpContext作为方法的参数) 23 objParameter = new object[1]; 24 objParameter[0] = context; 25 26 //获取参数的数据类型 27 objParamType = new Type[1]; 28 objParamType[0] = objParameter[0].GetType(); 29 30 //获取指定的方法对象 31 objMethod = objMethodType.GetMethod(strAction, objParamType); 32 33 //如果存在,则进行调用并返回给前台页面 34 if (objMethod != null) 35 { 36 context.Response.Write((string)objMethod.Invoke(this, objParameter)); 37 } 38 } 39 else 40 { 41 //异常提示信息 42 context.Response.Write(MESSAGE); 43 } 44 } 45 46 /// <summary> 47 /// 48 /// </summary> 49 public bool IsReusable 50 { 51 get 52 { 53 return false; 54 } 55 } 56 57 /// <summary> 58 /// 获取指定键值的参数值 59 /// </summary> 60 /// <param name="context">HttpContext</param> 61 /// <param name="key">指定键</param> 62 /// <returns>String</returns> 63 public String GetRequest(HttpContext context, String key) 64 { 65 String strRet = ""; 66 67 //如果指定键值的参数存在,则获取 68 if (context.Request[key] != null) 69 { 70 strRet = context.Request[key].ToString(); 71 } 72 73 return strRet; 74 }
这里有几个注意点:在实现ProcessRequest方法中,页面参数传值的获取上我这里只获取了Action参数的值:strAction = GetRequest(context, "Action");也就是要执行的方法名,而在实际应用中传值参数不止一个,不同的调用方法有不同的参数,当然在这里也可以全部获取存入数组中最后交给Invoke,但个人觉得没有必要在这在获取所有的传值,仅Action足已,因为Action是固定的,其他的参数可以交由要处理的Action方法去接收,怎么接收?我把HttpContext作为要执行的方法的参数传进去了:objParameter[0] = context;在执行定指的方法里边可以通过context对象再次获取页面传值,这样做的好处是代码清晰,分工明确,业务逻辑清晰简单。
(3).再看BaseHandler的子类,假设我们现在对用户管理这一模块来实现,该模块有用户登录、添加、删除、修改、列表等功能,将这些功能的实现方法统一放在UserHandler.ashx类中,在该类中只做了简单用户登录演示,其他功能都可触类旁通,代码如下:

1 /// <summary> 2 /// UserHandler 的摘要说明 3 /// </summary> 4 public class UserHandler : BaseHandler 5 { 6 /// <summary> 7 /// 用户登录处理 8 /// </summary> 9 /// <param name="context"></param> 10 /// <returns></returns> 11 public String Login(HttpContext context) 12 { 13 String strAccount = ""; 14 String strPassword = ""; 15 String strRet; 16 17 try 18 { 19 20 //获取页面传值 21 strAccount = GetRequest(context, "Account"); 22 strPassword = GetRequest(context, "Password"); 23 24 if (strAccount != "" && strPassword != "") 25 { 26 //简单起见,仅返回传递过来的用户账号与密码 27 //在实际应用中利用传递过来的参数值调用业务逻辑层的方法来完成客户端的请求 28 strRet = String.Format("账号:{0},密码:{1}", strAccount, strPassword); 29 } 30 else 31 { 32 //返回提示信息 33 strRet = "未能正确获取参数!"; 34 } 35 36 return strRet; 37 } 38 catch (Exception ex) 39 { 40 throw ex; 41 } 42 } 43 44 }
在这里我们只有一个Login方法参数为HttpContext类,而在这个方法里边我们可以通过HttpContext类再次获取除了Action以外的页面传值,代码应该是相当清晰的,注意在这里不需要再实现ProcessRequest与IsReusable方法,可以直接删去。
(4).Html网站前端调用UserHandler.ashx代码:

1 <script type="text/javascript"> 2 $(document).ready(function () { 3 $("#btnLogin").click(function () { 4 var account; 5 var password; 6 7 account = $("#txtUserName").val(); 8 password = $("#txtPassword").val(); 9 10 $.ajax({ 11 type: "POST", 12 async: true, 13 url: "Handler/UserHandler.ashx", 14 dataType: "text", 15 data: { Action: "Login", Account: account, Password: password }, 16 success: function (data) { 17 alert(data); 18 }, 19 error: function (xhr) { 20 alert(xhr.statusText); 21 return false; 22 } 23 }); 24 }); 25 }); 26 </script>
调用URL:UserHandler.ashx,传值参数:data: { Action: "Login", Account: account, Password: password },除了Login方法外还有账号与密码,坚持一切从简原则,回调函数只是alert了一下返回结果,从UserHandler.ashx的Login方法中不难看出,如果调用成功alert的应该是:"账号:你输入的账号,密码:你输入的密码"。到此一条路线已打通,其他的功能实现照样画葫芦就行。
(5).关于方法重载,我在百度的时候也看到过类似的介绍,也给了我很多的启示,但很多人都问到方法重载的问题如何解决,就没有下文了,其实实现重载也很简单,在同一方法里边根据你获取参数的多少与不同,间接的去调用重载方法不就解决问题了吗?