本文是對工做中的項目進行代碼優化(完善登錄驗證的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方法嗎?望各位大牛看官提點。