本文是对工做中的项目进行代码优化(完善登录验证的AOP切面编程)时,所遇到的各类解决方案思考过程。html
项目背景:由ashx+nvelocity构建的简单B/S问卷系统,现须要优化登陆验证环节(时隔若干个月在回顾代码果真是一个痛苦的过程~)web
nvelocity是velocity框架针对.net的版本,核心是拼html字符串后返回客户端,与MVC的先后端代码隔离有殊途同归之妙。加之通常处理程序ashx不须要像asp.net那样走生成控件树的过程,执行上更是省时省力。故简单系统用ashx+nvelocity的形式构建笔者我的仍是比较推荐的。若是那么在乎访问地址(如www.abc.com/news/index.ashx?id=234)中的ashx后缀很差看,彻底能够经过模块(HttpModule)来实现url重写。正则表达式
本文讨论的是:如何在ashx中体现AOP切面编程思想?数据库
(1)回顾asp.net,全部页面继承自Page类,可经过Page的子类来实现AOP。原来是:Default : Page,切面插入后是:Default : LoginCheckPage,LoginCheckPage : Page。如此便能在LoginCheckPage类中编写登陆验证的代码,且能实现全部须要验证页面的有效解耦——解耦是相对于专门写一个LoginCheck类,并在各个Default页面作验证(如LoginCheck.Check(context))而言,更利于修改与拓展。编程
(2)回顾MVC,能够依样画葫芦像上述asp.net那样,原来是:HomeController:Controller,切面插入后是:HomeController:LoginCheckController,LoginCheckController:Controller。除此以外,还能利用类/方法头上的特性标签来作AOP。后端
在asp.net与MVC中的AOP体现还有许多作法,此处抛砖引玉、仅为比对ashx的AOP作思考:上述方法在ashx中能行得通吗? 能!但要作些微调:cookie
(独写一个LoginCheck类,而后在每一个须要验证的ashx.pr()中加上LoginCheck.Check(context)实在不是长久之计,故本文就不另说了)session
第一种尝试:(没错,本文最后一次才尝试成功,不过写出尝试过程也是为了将本身所走的弯路作下记录,且但愿能给读者更多思考的提示,感谢坚持读完三种尝试的朋友。)mvc
利用HttpModule。相似url重写那样,url重写不都是每次请求一来就作处理吗,那Module应该也能作登录吧——二者差别:普通的url重写不涉及客户端隔离、不考虑请求的资源,登陆验证要作客户端隔离(cookie)、要考虑请求的资源(并非全部资源都不给访问,有的是游客级别就行的)。框架
对于要考虑请求资源的差别,若是恶心一点,能够在代码中写死(可优化成在webcofig、其余配置文件、数据库存储)来作差别化处理——以正则表达式匹配请求地址,用来隔离须要验证登录的请求与不须要验证的请求。
对于客户端隔离,可否直接在Module中用session?首先要使HttpModule继承自IReadonlySessionState/IRequiresSessionState接口(HttpHandler也是如此),以便在走管道的时候能被.net认出来你这个Module想用session。注册到BeginRequest事件。别忘了还要注册到webconfig。一切就绪,调试,报错——HttpApplication中的Session属性报错,未将对象引用设置到对象实例。是否是注册的事件错了?我查了一遍HttpApplication管道中的19个事件,最佳的切入点在第10-11个事件之间,也就是+=PostAcquireRequest,才能在获取Session以后、在执行ashx以前作登录验证。
然而并无什么X用……依旧未将对象引用设置到对象实例。怎么仍是没有呢,奇了怪了。
又是一边各类查,查到一句话说得好:Module是应用程序级的事儿,是过滤做用,而Session是页面级的事儿,是要根据发来的请求作不一样的处理,故在Module中用Session本就不是最佳方案。故放弃Module这条弯路。
第二种尝试:
自定义继承自IHttpHandler的ashx。原来:Index:IHttpHandler,优化后:Index:LoginCheckHandler,LoginCheckHandler:IHttpHandler。学的上述asp.net与mvc中的插入到继承树的方法。但调试结果是根本不走Index的ProcessRequest(),直接走完LoginCheckHandler.ProcessRequest()就返回了,客户端就是空白一片。究其缘由:实现IHttpHandler的通常处理程序(不管是Index,仍是LoginCheckHandler),都只会执行一次ProcessRequest()。
第三种尝试:
在第二种的基础上修改成:LoginCheckHandler中的ProcessRequest()改成virtual,并在Index子类中override重写,并在Index.ProcessRequest()中调用base.ProcessRequest(context)。执行的时候,程序会由于看到override而忽略父类的PR方法,而Index子类中的base.PR()又要求程序先走父类的PR方法,且结合Response.Redirect()的当即输出特性(先Flush,在End),可使得不知足登陆验证条件的请求被挡在门外。小功告成!
麻烦的是,要修改子类为override,且在子类中存在base.PR()代码(也只比简单粗暴的调用LoginCheck.Check(context)来验证减小了一些些耦合度),那么还有更好的AOP方法吗?望各位大牛看官提点。