ASP.NET进阶(8):HttpModule和HttpApplication
??? 前面三節講了控件的構造、呈現和數據綁定,我想該差不多了。本想講一個自定義控件來終結控件部分,但是我個人不太喜歡控件這些東西,所以也就懶的寫相關的內容,抱歉了。雖然我不喜歡使用控件,但我還是喜歡整個WebForm的設計。一個字:“太神了”。前面章節將Page生命周期的時候有朋友評論說內容太少了,今天開始就從來圍繞生命周期的話,講講相關的內容吧。
??? IHttpModule是個什么東西呢?對我們Web開發有什么用呢?
??? 先從名字來看他是一個接口,接口就是讓人來繼承的,我們要用它就得繼承他,并實現他的方法。Module的意思是模塊、組件的意思。如果說我們實現了這個接口,并配置了web.config,讓IIS的知道我們的web程序使用了這個組件;那么我們的程序是不是就比默認的web程序多了個組件?!顯然,而且在必要的時候會調用我們組件里定義的方法,這就是HttpModule的用處。說白了,就是我們給IIS寫擴展,但該擴展僅僅是針對于使用了(配置config)的web程序。其實每個web應用程序都是一個IIS進程,而這個進程的配置文件就是web.config。
??? 弄明白了他的意義,我們就開始!我們創建一個Web應用程序,并創建一個類,繼承IHttpModule,并實現他的方法,在Config的Modules節點里<add name="" type=""/>,OK!
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <httpModules> <add name="MyHttpModule" type="WebApplication1.MyHttpModule,WebApplication1"/> </httpModules> </system.web><system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="MyHttpModule" type="WebApplication1.MyHttpModule,WebApplication1"/><!--注意:type="類的FullName,類所在的dll名"--> </modules> </system.webServer> </configuration>
web.config的配置有2個,上面的那個是給非IIS7用的,下面的顯然就是給IIS7用的。啟動程序,what happend?! 是不是頁的頭部多了個1,有木有!!我們打開任何頁面都會有個1,說明我們的模塊起到作用了,也說明每個請求都會執行HttpModule的Init方法?是不是呢?
我們把代碼改一下:
?
分別給Init和OnBeginRequest 兩個方法加斷點,重新編譯下,然后F5看看。Init只走1次,而OnBeginRequest卻走了3次,ur的值l分別是? default.aspx?? style.css 和 favorite.ico;可以看出任何url請求,包括靜態文件,都會經過執行我們定義的事件方法!看來這要比只處理aspx慢不少!
Init的必須走一次啊,要不然事件不被訂閱3次了?,但為什么只走1次呢?這到底是為什么呢? 呵呵,其實很簡單,MyHttpModule就實例化一次哦,實例化后執行Init初始化,然后就駐留在應用程序池了,直到應用程序池被回收,或他被各種原因搞崩潰;而OnBeginRequest是被HttpApplication類的BeginRequest事件訂閱的。事件訂閱是個什么概念?事件是個特殊的委托,委托是個什么概念?委托是個方法指針。所以,只要委托被執行,就會執行他指向的方法體,也就是OnBeginRequest,可見OnBeginRequest的執行,是和HttpApplication的BeginRequest有關系的,和MyHttpModule本身已經沒關系了。
??? 走了3次說明3個Request都執行了BeginRequest,難道每個請求都實例化一個HttpApplication?從名字我就能看出不會的,因為Application(應用程序)嘛,我們目前運行的就一個,怎么會不斷的實例化!想刨根問題,徹底整明白,就得翻出Framework的源碼,調試!
(------------聲明,下面的源碼可以不用完全理解,也可以跳過,只要知道跟Request有關就行了------------)
下面來調查下HttpApplication的初始化過程!
用Reflector查閱System.Web名字空間下的類,可以看到HttpApplicationFactory類,他負責HttpApplication的創建。當我們啟動站點后,第一次的時候比較慢,為什么呢? 因為初始化的構建工作。
System.Web.Complilation名字空間下有一堆的構建類,其中就有構建Global.asax的,也就是我們的HttpApplication類,然后緩存到Factory的堆棧里,我們需要的時候pop出來。 (你可能有疑問,pop了不就沒了嗎? 其實app在執行的時候還會push回去,詳見HttpApplication.ReleaseAppInstance方法)
HttpApplicationFactory有個GetApplicationInstance方法,就是用來獲取HttpApplication的:?
跟蹤這個方法,我們可以斷定,Application是被緩存起來的,不是每次都是實例化的。
通過Reflector的分析,我們能發現這個GetApplicationInstance方法是被HttpRuntime.ProcessRequestNow調用的,終于回到我們的Request來了。
很好,上面的代碼讓我們看清了一個Request的執行過程。而每個Request都會走這個方法!HttpRuntime有個RequestQueue(請求隊列),會依次執行所有的Request。終于知道為什么走3次了吧:) 就是application被用了3次。感興趣的同學可以再去跟蹤RequestQueue,我就不貼了。
另外,HttpApplication,意味著他是整個站點的boss,我們定義的myhttpmodule不過是他眾多Modules里的其中之一。而且我們也可以定義多個module,config里面add多個就可以了。
在Application初始化的過程中,有初始化module的一段,我貼出來大家看看:
其中CreateModules就是從web.config的module節點實例化我們配置的module
internal HttpModuleCollection CreateModules() { HttpModuleCollection modules = new HttpModuleCollection(); foreach (HttpModuleAction action in this.Modules) { modules.AddModule(action.Entry.ModuleName, action.Entry.Create()); } modules.AddModule("DefaultAuthentication", new DefaultAuthenticationModule()); return modules; }
最后,初始化所有的module,包括系統的一些module。
private void InitModulesCommon() { int count = this._moduleCollection.Count; for (int i = 0; i < count; i++) { this._currentModuleCollectionKey = this._moduleCollection.GetKey(i); this._moduleCollection[i].Init(this);//初始化每個HttpModule } this._currentModuleCollectionKey = null; this.InitAppLevelCulture(); }
(--------------跳到這里----------------)
??? HttpApplication類公開了很多事件。上面的示例程序用到了BeginRequest,這個事件是最先開始執行的事件;其作用很明白,就是Request開始執行時,我們要準備點什么?或許你可能需要urlrewriter:)。下面插播“事件廣告”,廣告之后馬上飛來。
上面我只是簡單的提了一句“事件是特殊的委托”,并沒有詳細說為什么特殊。不知道同學你是否理解事件的意義呢?事件的意義是什么?
我是這么理解的。“事件”,代表著一件事情的發生。我們打一個比方,我把每天的生活設計成一個類。那么,一天的生活包含什么?包含從早到晚,包含很多事情要去做,甚至包含一些固定的事情。
細品這3個包含:
從早到晚,意味著這是一個過程,從頭到尾,從始到終;很多事情要去做,說明在這個過程中要執行很多事;而一些固定的事情,比如吃飯,睡覺。
我們可以把早和晚,看作是構造函數和析構函數;把很多事情要做看作是事件;把固定的事情看作是方法。
因為每個人一天的生活都不一定是相同的,所以每天要去做的事我們沒法寫成方法!我們最多只能定義一些固有的模式的方法抽象,比如起床后做什么,午飯后做什么,睡覺前做什么。這不就是事件么?
我們在設計類到時候,如果類的使用有時候也涉及到一些執行過程的問題,而在這個過程中會發生一些未知的事情(未知意味著由外部類來提供,自己提供就是已知了),我們便把這些未知設計成抽象的方法。
由于過程的順序是固定的,比如午飯后做什么就必須實在午飯后,所以午飯后做什么事件不能被別人在早上使用(你就是上帝不能把午飯的事情給我挪到早飯,挪了就叫早飯后了)。
同樣的道理,事件的執行不能由外部來決定,這就是事件有別于委托的地方(委托沒有使用限制,隨時隨地都可以用),這也是事件的意義。 整個過程也就是所謂的“生命周期”。
代碼和現實就是這么的一致,耐人尋味。
廣告回來~~ 繼續看HttpApplication的事件,我把他們按執行的順序貼了出來;從名字就能看出大概的作用。有些我從來沒用過:)
BeginRequest??????? //請求開始
AuthenticateRequest???
PostAuthenticateRequest???
AuthorizeRequest???????
PostAuthorizeRequest
ResolveRequestCache???
PostResolveRequestCache
PostMapRequestHandler
AcquireRequestState??? //獲得請求狀態,這時候已經有session了
PostAcquireRequestState
PreRequestHandlerExecute??? //準備交給HttpHandler處理
Error??????????? //請求出現了異常!!!
PostRequestHandlerExecute
ReleaseRequestState??? //發布請求的狀態
PostReleaseRequestState
UpdateRequestCache
PostUpdateRequestCache
EndRequest??????? //結束請求
PreSendRequestHeaders??? //準備發送請求頭信息,在這我們還能修改內容
PreSendRequestContent??? //準備發送請求內容,這里就改不了了
這才是真正的整個生命周期,是不是!而面試題一般考的是Page類的生命周期,這已經過時了,web開發又不光Webform,所以考page類,沒技術含量:)
在HttpApplication里,把這些事件作為Step,Step by Step的執行下去,下面是HttpApplication構建Step的代碼:
internal override void BuildSteps(WaitCallback stepCallback) { ArrayList steps = new ArrayList(); HttpApplication app = base._application; bool flag = false; UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings; flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0); steps.Add(new HttpApplication.ValidatePathExecutionStep(app)); if (flag) { steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); } app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps); app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps); steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps); app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps); steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); steps.Add(new HttpApplication.CallFilterExecutionStep(app)); app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); this._endRequestStepIndex = steps.Count; app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); steps.Add(new HttpApplication.NoopExecutionStep()); this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; steps.CopyTo(this._execSteps); this._resumeStepsWaitCallback = stepCallback; }
從構建的順序我們也能看出執行的順序,每個Step都有一個Execute的方法,挨個執行下去,如果程序出現異常,則直接跳出。而我們的Page執行是在CallHandlerExecutionStep這個Step里。
??? 好啦,就講到這唄?今天漢字比較少,代碼比較多;您還有啥不能明白的就評論里聊吧。沒用過的同學動寫個吧,HttpModule是個好東西哦。PS:我們公司面試的筆試題里有道題“給一個正在運行的網站增加一個異常監控功能,該怎么實現?” 你能想到怎么做嗎?:)
轉載于:https://www.cnblogs.com/mad/archive/2011/04/11/asp-net-httpmodule-and-httpapplication.html
總結
以上是生活随笔為你收集整理的ASP.NET进阶(8):HttpModule和HttpApplication的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新的开始 和一些总结
- 下一篇: Cannot retrieve mapp