【譯】ASP.NET應用程序和頁面生命周期


為何翻譯此文

  一、此文是Code Project社區2010年4月ASP.NET板塊的最佳文章,說明了此文的份量;

  二、鍛煉自己的英文技術文章翻譯能力,提高英文技術文檔閱讀能力;

  三、了解掌握ASP.NET頁面生命周期是非常必要的,這有助於我們更加靈活的控制頁面,以我們需要的方式編程開發;

關於原文作者

原文作者: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頁面事件

一、概述

  在本文中,我們會試着了解用戶在瀏覽器中發出一個Web請求 到 這個請求被響應並顯示在瀏覽器中的過程中究竟會發生哪些不同的事件。首先,我們先試着了解一下ASP.NET請求的兩個大體上的步湊,然后我們將關注點轉移到從'HttpHandler'、'HttpModule'以及ASP.NET頁面對象所觸發的不同事件上。當我們進入這個事件之旅時,我們也會試着明白在請求處理的每個事件當中我們可以做什么業務邏輯處理操作。

二、大體上的兩步處理流程

  大體上,ASP.NET請求的處理流程分為如下圖所示的兩個步湊。用戶發送一個請求到IIS服務器時:

  (1)ASP.NET會創建一個能夠處理請求的環境。換句話說,它會創建一個包含請求、響應以及上下文對象的應用程序對象來處理這個請求。

  (2)一旦ASP.NET環境被創建,用戶請求就會通過由modules(管道)、handlers(處理程序)和page objects(頁面對象)觸發的一系列事件進行處理。簡而言之,我們暫且將此步湊稱為MHPM(Module、Handler、Page和Module Event)。

Two Main Steps

 

  圖1 ASP.NET的兩個主要處理步湊

  在接下來的部分中,我們會深入地理解這兩個主要的步湊。

三、ASP.NET環境的創建

  第一步:用戶請求到達IIS后,IIS首先會檢查哪一個ISAPI擴展能夠處理這個請求,這會取決於文件的后綴名。例如:如果請求的是一個'.aspx'的頁面,那么就會被傳遞到'aspnet_isapi.dll'來進行處理。

  第二步:如果這是該網站的首次請求,那么一個稱為'ApplicationManager'的類會首先創建一個該網站可以運行的應用程序域(App Domain)。正如我們所知,應用程序域隔離部署在同一台IIS服務器上的兩個不同的Web應用程序。因此,即使其中一個應用程序域出現了錯誤,也不會影響其他應用程序域的正常運作。

Note:下面的內容是我補充的,非原文內容。

.NET平台下,程序集並沒有直接加載進 進程 中(傳統的Win32程序是直接承載的)。.NET可執行程序承載在進程的一個邏輯分區中,術語稱應用程序域(簡稱AppDomain)。應用程序域是.NET引入的一個新概念,它比進程所占用的資源要少,可以被看作是一個 輕量級的進程

在一個進程中可以包含多個應用程序域,一個應用程序域可以裝載一個可執行程序(*.exe)或者多個程序集(*.dll)。這樣可以使應用程序域之間實現深度隔離,所以:即使進程中的某個應用程序域出現錯誤,也不會影響其他應用程序域的正常運作。

更多關於AppDomain的介紹,請自行搜索,這里不再贅述。

  第三步:在新創建的應用程序域中,會創建ASP.NET的宿主環境,也就是HttpRuntime對象。一旦宿主環境被創建完成,ASP.NET最核心的對象如HttpContextHttpRequestHttpResponse對象都會被創建好。

  第四步:一旦所有核心的ASP.NET對象被創建好,HttpApplication對象就會隨之被創建來服務這個請求。如果你的系統中存在一個global.asax文件,那么這個global.asax文件的對象也會被創建。但是,需要注意的是你的global.asax需要繼承自HttpApplication類。

  注意:在一個ASP.NET頁面第一次附加到網站,一個HttpApplication實例便隨之產生。為了最大化得提高處理性能,HttpApplication的實例將會被復用以處理多個請求。

Note:下面的內容是我補充的,非原文內容。

Global.asax 文件(也稱作 ASP.NET 應用程序文件)是可選文件,包含用於響應 ASP.NET 或 HttpModule 引發的應用程序級別事件的代碼。(換句話說,我們可以自定義后面我們所要介紹的一些事件,因為請求處理流程會經歷后面的10多個事件,我們可以寫代碼來自定義其中的一些事件,加一些我們想做的業務邏輯操作,比如:URL重寫、身份驗證、圖片水印等等。)

如果不定義該文件,ASP.NET 頁框架假設您未定義任何應用程序或會話事件處理程序。

  第五步:此時HttpApplication對象將會被分配給一系列的ASP.NET核心對象來處理請求的頁面。

  第六步:這時,HttpApplication開始通過HTTP管道事件、處理程序(Handlers)和頁面事件來處理請求了。也就是說:它會觸發 MHPM 中的事件來處理請求。

  么么嗒,你也可以通過下圖來詳細地了解這幾個步湊。

Create Environment Steps

  圖2 ASP.NET環境的創建

  下圖則形象地展示了在一個ASP.NET請求過程中的重要內部對象模型。最高層是ASP.NET運行時,它創建了一個應用程序域(AppDoamin),下層則創建了一個包含request、response以及context對象的HttpRuntime。

General Explain Steps

 圖3 ASP.NET請求過程中的內部對象模型

四、通過MHPM觸發的事件處理請求

  一旦HttpApplication創建好,它就開始處理請求了。它經歷了三個不同的部分:HttpModulePageHttpHandler。當它經過這些部分時,它將調用不同的事件,而這些事件的邏輯處理還可以由開發者來進行擴展和增加自定義處理。

  在進一步深入了解之前,讓我們先來了解一下什么是HttpModuleHttpHandlers。他們幫助我們在ASP.NET頁面處理過程的前后注入自定義的邏輯處理。他們之間主要的差別在於:

  • 如果你想要注入的邏輯是基於像'.aspx','.html'這樣的擴展名,那么你可以使用HttpHandler。換句話說,HttpHandler是一個基於處理器的擴展。

HttpHandler Show

圖4 HttpHandler

  • 如果你想要在ASP.NET管道事件中注入邏輯,那么你可以使用HttpModule。也可以說,HttpModule是一個基於處理器的事件。

HttpModule Show

圖5 HttpModule

  你可以從這里了解更多關於他們這對好基友之間的差別。

  下面是請求處理過程的邏輯流程,其中有4個重要的步湊,解釋如下:

  第一步(M:HttpModule):客戶端請求開始被處理。在ASP.NET引擎執行和創建HttpModule觸發事件(在此過程中,你也可以注入自定義邏輯)之前,有6個事件你可以在頁面對象創建之前來使用,它們分別是:BeginRequestAuthenticateRequestAuthorizeRequestResolveRequestCacheAcquireRequestState 以及 PreRequestHandlerExecute

  第二步(H:HttpHandler):一旦以上6個事件被觸發后,ASP.NET引擎就將會調用 ProcessRequest 事件,即使你已經在項目中實現了 HttpHandler

  第三步(P:ASP.NET Page):一旦HttpHandler邏輯執行,ASP.NET頁面對象就被創建了。而ASP.NET頁面被創建,一系列的事件也會隨之被觸發,它們可以幫助我們自定義邏輯注入到這些事件里邊。在此過程中,有6個重要事件給我們提供了占位符,以便我們在ASP.NET頁面中寫入邏輯,它們分別是:InitLoadValidateRender Unload。你可以通過記住單詞SILVER來記憶這幾個事件,S—Start(沒有任何意義,僅僅是為了形成一個單詞),I(Init)、L(Load)、V(Validate)、E(Event)、R(Render)。

  第四步(M:HttpModule):一旦頁面對象執行結束並從內存中被卸載,HttpModule提供了提交返回頁面的執行事件,同樣,在這些事件中也可以被注入自定義的返回處理邏輯。這里有4個重要的提交處理事件:PostRequestHandlerExecuteReleaserequestStateUpdateRequestCache以及EndRequest

  下圖形象地展示了上面的四個步湊。

MHPM

圖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 這是將輸出發送到客戶端瀏覽器之前的最后一個階段。

六、一個簡單的示例

  我們可以通過一個示例程序代碼來展示以上介紹的那些事件是怎樣被最終觸發的。在這個示例中,我們已經創建了一個HttpModuleHttpHandler,並且也在所有的事件中通過添加自定義邏輯代碼展示了一個簡單的響應。

  下面是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>") ;
}
} 
}
View Code

  下面是HttpHandler類的一個代碼片段,它跟蹤了ProcessRequest事件。

public class clsHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
clsHttpModule.objArrayList.Add("HttpHandler:ProcessRequest");
context.Response.Redirect("Default.aspx");
}
}
View Code

  同上,我們也可以跟蹤來自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");
}
}
View Code

  下圖則顯示了上面我們所討論的所有事件的執行順序。

A Sample Demo

圖7 示例結果—事件的執行次序

七、詳解ASP.NET頁面事件

  在上面的部分中,我們已經了解了一個ASP.NET頁面請求事件的整體流程。那么,在其中一個最重要的部分就是ASP.NET頁面,但是我們並沒有對其進行詳細討論。因此,我們在此深入地了解一下ASP.NET頁面事件。

  每一個ASP.NET頁都有2個部分:一個是在瀏覽器中進行顯示的部分,它包含了HTML標簽、viewstate形式的隱藏域 以及 在HTML input中的數據。當這個頁面被提交到服務器時,這些HTML標簽會被創建到ASP.NET控件,並且viewstate還會和表單數據綁定在一起。一旦你在后置代碼中得到所有的服務器控件,你可以執行和寫入你自己的邏輯並呈現給客戶瀏覽器。

ASP.NET Page

圖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 任何你想做的清理工作都可以在這里執行。

Page Events in Detail

圖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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM