![]() |
原文作者:Shivprasad koirala |
原文地址:http://www.codeproject.com/Articles/73728/ASP-NET-Application-and-Page-Life-Cycle |
|
他的介紹:微軟MVP(ASP/ASP.NET),現在是印度一家小型在線教育公司的CEO。他非常積極地在制作在線培訓視頻,寫技術書籍及做企業培訓。 |
- 概述
- 大體上的兩步處理流程
- ASP.NET環境的創建
- 通過MHPM觸發的事件處理請求
- 在什么事件中我們可以做什么?
- 一個簡單的示例
- 詳解ASP.NET頁面事件
圖2 ASP.NET環境的創建
下圖則形象地展示了在一個ASP.NET請求過程中的重要內部對象模型。最高層是ASP.NET運行時,它創建了一個應用程序域(AppDoamin),下層則創建了一個包含request、response以及context對象的HttpRuntime。
圖3 ASP.NET請求過程中的內部對象模型
四、通過MHPM觸發的事件處理請求
一旦HttpApplication創建好,它就開始處理請求了。它經歷了三個不同的部分:HttpModule、Page和HttpHandler。當它經過這些部分時,它將調用不同的事件,而這些事件的邏輯處理還可以由開發者來進行擴展和增加自定義處理。
在進一步深入了解之前,讓我們先來了解一下什么是HttpModule和HttpHandlers。他們幫助我們在ASP.NET頁面處理過程的前后注入自定義的邏輯處理。他們之間主要的差別在於:
- 如果你想要注入的邏輯是基於像'.aspx','.html'這樣的擴展名,那么你可以使用HttpHandler。換句話說,HttpHandler是一個基於處理器的擴展。
圖4 HttpHandler
- 如果你想要在ASP.NET管道事件中注入邏輯,那么你可以使用HttpModule。也可以說,HttpModule是一個基於處理器的事件。
圖5 HttpModule
你可以從這里了解更多關於他們這對好基友之間的差別。
下面是請求處理過程的邏輯流程,其中有4個重要的步湊,解釋如下:
第一步(M:HttpModule):客戶端請求開始被處理。在ASP.NET引擎執行和創建HttpModule觸發事件(在此過程中,你也可以注入自定義邏輯)之前,有6個事件你可以在頁面對象創建之前來使用,它們分別是:BeginRequest、AuthenticateRequest、AuthorizeRequest、ResolveRequestCache、AcquireRequestState 以及 PreRequestHandlerExecute。
第二步(H:HttpHandler):一旦以上6個事件被觸發后,ASP.NET引擎就將會調用 ProcessRequest 事件,即使你已經在項目中實現了 HttpHandler。
第三步(P:ASP.NET Page):一旦HttpHandler邏輯執行,ASP.NET頁面對象就被創建了。而ASP.NET頁面被創建,一系列的事件也會隨之被觸發,它們可以幫助我們自定義邏輯注入到這些事件里邊。在此過程中,有6個重要事件給我們提供了占位符,以便我們在ASP.NET頁面中寫入邏輯,它們分別是:Init、Load、Validate、Render 和 Unload。你可以通過記住單詞SILVER來記憶這幾個事件,S—Start(沒有任何意義,僅僅是為了形成一個單詞),I(Init)、L(Load)、V(Validate)、E(Event)、R(Render)。
第四步(M:HttpModule):一旦頁面對象執行結束並從內存中被卸載,HttpModule提供了提交返回頁面的執行事件,同樣,在這些事件中也可以被注入自定義的返回處理邏輯。這里有4個重要的提交處理事件:PostRequestHandlerExecute、ReleaserequestState、UpdateRequestCache以及EndRequest。
下圖形象地展示了上面的四個步湊。
圖6 MHPM過程
五、在什么事件中我們可以做什么?
一個十分有價值的問題就是在什么事件中我們又可以做些什么?下表就展示了這個問題的答案:
Section | Event | Description |
HttpModule | BeginRequest | 此事件標志着一個新的請求,它保證在每個請求中都會被觸發。 |
HttpModule | AuthenticateRequest | 此事件標志ASP.NET運行時准備驗證用戶。任何身份驗證代碼都可以在此注入。 |
HttpModule | AuthorizeRequest | 此事件標志ASP.NET運行時准備授權用戶。任何授權代碼都可以在此注入。 |
HttpModule | ResolveRequest | 在ASP.NET中我們通常使用OutputCache指令做緩存。在這個事件中,ASP.NET運行時確定是否能夠從緩存中加載頁面,而不是從頭開始生成。任何緩存的具體活動可以被注入這里。 |
HttpModule | AcquireRequestState | 此事件標志着ASP.NET運行時准備獲得Session會話變量。可以對Session變量做任何你想要做的處理。 |
HttpModule | PreRequestHandlerExecute | 恰好在ASP.NET 開始執行事件處理程序前發生。可以預處理你想做的事。 |
HttpHandler | ProcessRequest | HttpHandler邏輯被執行。在這個部分我們將為每個頁面擴展寫需要的邏輯。 |
Page | Init | 此事件發生在ASP.NET頁面且可以用來: 1、動態地創建控件,如果你一定要在運行時創建控件; 2、任何初始化設置 3、母版頁及其設置 在這部分中我們沒有獲得viewstate、postedvalues及已經初始化的控件。 |
Page | Load | 在這部分ASP.NET控件完全被加載且在這里你可以寫UI操作邏輯或任何其他邏輯。NOTE:這個事件也是我們最常見且最常用的一個事件。 |
Page | Validate | 如果在頁面上你有驗證器,你同樣想在這里做一下檢查。 |
Page | Render | 是時候將輸出發送到瀏覽器。如果你想對最終的HTML做些修改,你可以在這里輸入你的HTML邏輯。 |
Page | Unload | 頁面對象從內存中卸載。 |
HttpModule | PostRequestHandlerExecute | 可以注入任何你想要的邏輯,在處理程序執行之后。 |
HttpModule | ReleaseRequestState | 如果你想要保存對某些狀態變量的更改,例如:Session變量的值。 |
HttpModule | UpdateRequestCache | 在結束之前,你是否想要更新你的緩存。 |
HttpModule | EndRequest | 這是將輸出發送到客戶端瀏覽器之前的最后一個階段。 |
六、一個簡單的示例
我們可以通過一個示例程序代碼來展示以上介紹的那些事件是怎樣被最終觸發的。在這個示例中,我們已經創建了一個HttpModule和HttpHandler,並且也在所有的事件中通過添加自定義邏輯代碼展示了一個簡單的響應。
下面是HttpModule類,它跟蹤了所有的事件並將其添加到了一個全局的集合中。

public class clsHttpModule : IHttpModule { ...... void OnUpdateRequestCache(object sender, EventArgs a) { objArrayList.Add("httpModule:OnUpdateRequestCache"); } void OnReleaseRequestState(object sender, EventArgs a) { objArrayList.Add("httpModule:OnReleaseRequestState"); } void OnPostRequestHandlerExecute(object sender, EventArgs a) { objArrayList.Add("httpModule:OnPostRequestHandlerExecute"); } void OnPreRequestHandlerExecute(object sender, EventArgs a) { objArrayList.Add("httpModule:OnPreRequestHandlerExecute"); } void OnAcquireRequestState(object sender, EventArgs a) { objArrayList.Add("httpModule:OnAcquireRequestState"); } void OnResolveRequestCache(object sender, EventArgs a) { objArrayList.Add("httpModule:OnResolveRequestCache"); } void OnAuthorization(object sender, EventArgs a) { objArrayList.Add("httpModule:OnAuthorization"); } void OnAuthentication(object sender, EventArgs a) { objArrayList.Add("httpModule:AuthenticateRequest"); } void OnBeginrequest(object sender, EventArgs a) { objArrayList.Add("httpModule:BeginRequest"); } void OnEndRequest(object sender, EventArgs a) { objArrayList.Add("httpModule:EndRequest"); objArrayList.Add("<hr>"); foreach (string str in objArrayList) { httpApp.Context.Response.Write(str + "<br>") ; } } }
下面是HttpHandler類的一個代碼片段,它跟蹤了ProcessRequest事件。

public class clsHttpHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { clsHttpModule.objArrayList.Add("HttpHandler:ProcessRequest"); context.Response.Redirect("Default.aspx"); } }
同上,我們也可以跟蹤來自ASP.NET Page頁面的所有事件。

public partial class _Default : System.Web.UI.Page { protected void Page_init(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:Init"); } protected void Page_Load(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:Load"); } public override void Validate() { clsHttpModule.objArrayList.Add("Page:Validate"); } protected void Button1_Click(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:Event"); } protected override void Render(HtmlTextWriter output) { clsHttpModule.objArrayList.Add("Page:Render"); base.Render(output); } protected void Page_Unload(object sender, EventArgs e) { clsHttpModule.objArrayList.Add("Page:UnLoad"); } }
下圖則顯示了上面我們所討論的所有事件的執行順序。
圖7 示例結果—事件的執行次序
七、詳解ASP.NET頁面事件
在上面的部分中,我們已經了解了一個ASP.NET頁面請求事件的整體流程。那么,在其中一個最重要的部分就是ASP.NET頁面,但是我們並沒有對其進行詳細討論。因此,我們在此深入地了解一下ASP.NET頁面事件。
每一個ASP.NET頁都有2個部分:一個是在瀏覽器中進行顯示的部分,它包含了HTML標簽、viewstate形式的隱藏域 以及 在HTML input中的數據。當這個頁面被提交到服務器時,這些HTML標簽會被創建到ASP.NET控件,並且viewstate還會和表單數據綁定在一起。一旦你在后置代碼中得到所有的服務器控件,你可以執行和寫入你自己的邏輯並呈現給客戶瀏覽器。
圖8 ASP.NET頁的兩個部分
現在這些HTML控件會作為ASP.NET控件存活在服務器上,ASP.NET會觸發一系列的事件,我們也可以在這些事件中注入自定義邏輯代碼。根據你想要執行什么樣的任務/邏輯,我們需要將邏輯合理地放入這些事件之中。
注意:大部分的開發者直接使用Page_Load來干所有的事情,但這並不是一個好的思路。因此,無論是填充控件、設置ViewState還是應用主題等所有發生在頁面加載中的所有事情。因此,如果我們能夠在合適的事件中放入邏輯,那么毫無疑問我們代碼將會干凈很多。
順序 | 事件名稱 | 控件初始化 | ViewState可用 | 表單數據可用 | 什么邏輯可以寫在這里? |
1 | Init | No | No | No | 注意:你可以通過使用ASP.NET請求對象訪問表單數據等,但不是通過服務器控件。 動態地創建控件,如果你一定要在運行時創建;任何初始化設置;母版頁及其設置。在這部分中我們沒有獲得viewstate、提交的數據值及已經初始化的控件。 |
2 | Load View State | Not guaranteed | Yes | Not guaranteed | 你可以訪問View State及任何同步邏輯,你希望viewstate被推到后台代碼變量可以在這里完成。 |
3 | PostBackdata | Not guaranteed | Yes | Yes | 你可以訪問表單數據。任何邏輯,你希望表單數據被推到后台代碼變量可以在這里完成。 |
4 | Load | Yes | Yes | Yes | 在這里你可以放入任何你想操作控件的邏輯,如從數據庫填充combox、對grid中的數據排序等。這個事件,我們可以訪問所有控件、viewstate、他們發送過來的值。 |
5 | Validate | Yes | Yes | Yes | 如果你的頁面有驗證器或者你想為你的頁面執行驗證,那就在這里做吧。 |
6 | Event | Yes | Yes | Yes | 如果這是通過點擊按鈕或下拉列表的改變的一個回發,相關的事件將被觸發。與事件相關的任何邏輯都可以在這里執行。 PS:這個事件想必很多使用WebForm的開發人員都很常用吧,是否記得那些Button1_Click(Object sender,EventArgs e)? |
7 | Pre-render | Yes | Yes | Yes | 如果你想對UI對象做最終的修改,如改變屬性結構或屬性值,在這些控件保存到ViewState之前。 |
8 | Save ViewState | Yes | Yes | Yes | 一旦對服務器控件的所有修改完成,將會保存控件數據到View State中。 |
9 | Render | Yes | Yes | Yes | 如果你想添加一些自定義HTML到輸出,可以在這里完成。 |
10 | Unload | Yes | Yes | Yes | 任何你想做的清理工作都可以在這里執行。 |
圖9 ASP.NET Page事件流程
一張圖復習ASP.NET請求處理(自己補充,非原文內容)
翻譯中參考的資料
(1)碧血軒,《ASP.NET頁面生命周期》,http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html
(2)吳秦,《ASP.NET 應用程序與頁面生命周期(意譯)》,http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html
(3)風塵浪子,《C#綜合揭秘—細說進程、應用程序域與上下文之間的關系》,http://www.cnblogs.com/skynet/archive/2010/04/29/1724020.html
(4)菩提樹下的楊過,《溫故而知新:HttpApplication,HttpModule,HttpContext及Asp.Net頁生命周期》,http://www.cnblogs.com/yjmyzz/archive/2010/03/28/1698968.html
(5)MSDN,《ASP.NET頁面生命周期概述》,http://msdn.microsoft.com/zh-cn/library/ms178472.aspx
(6)皺華棟,《ASP.NET!=拖控件之2011版視頻教程》,http://bbs.itcast.cn/thread-8439-1-1.html