摘要:了解為 ASP.NET Web 頁(yè)面建立的事件模型,以及 Web 頁(yè)面轉(zhuǎn)變?yōu)?HTML 過(guò)程中的各個(gè)階段。ASP.NET HTTP 運(yùn)行時(shí)負(fù)責(zé)管理對(duì)象管道,這些對(duì)象首先將請(qǐng)求的 URL 轉(zhuǎn)換成 Page 類的具體實(shí)例,然后再將這些實(shí)例轉(zhuǎn)換成純 HTML 文本。本文將探討那些作為頁(yè)面生命周期標(biāo)志的事件,以及控件和頁(yè)面編寫(xiě)者如何干預(yù)并改變標(biāo)準(zhǔn)行為。 簡(jiǎn)介 對(duì)由 Microsoft? Internet 信息服務(wù) (IIS) 處理的 Microsoft? ASP.NET 頁(yè)面的每個(gè)請(qǐng)求都會(huì)被移交到 ASP.NET HTTP 管道。HTTP 管道由一系列托管對(duì)象組成,這些托管對(duì)象按順序處理請(qǐng)求,并將 URL 轉(zhuǎn)換為純 HTML 文本。HTTP 管道的入口是 HttpRuntime 類。ASP.NET 結(jié)構(gòu)為輔助進(jìn)程中的每個(gè) AppDomain 創(chuàng)建一個(gè)此類的實(shí)例。(請(qǐng)注意,輔助進(jìn)程為每個(gè)當(dāng)前正在運(yùn)行的 ASP.NET 應(yīng)用程序維護(hù)一個(gè)特定的 AppDomain。) HttpRuntime 類從內(nèi)部池中獲取 HttpApplication 對(duì)象,并安排此對(duì)象來(lái)處理請(qǐng)求。HTTP 應(yīng)用程序管理器完成的主要任務(wù)就是找到將真正處理請(qǐng)求的類。當(dāng)請(qǐng)求 .aspx 資源時(shí),處理程序就是頁(yè)面處理程序,即從 Page 繼承的類的實(shí)例。資源類型和處理程序類型之間的關(guān)聯(lián)關(guān)系存儲(chǔ)在應(yīng)用程序的配置文件中。更確切地說(shuō),默認(rèn)的映射集是在 machine.config 文件的 <httpHandlers> 部分定義的。但是,應(yīng)用程序可以在本地的 web.config 文件中自定義自己的 HTTP 處理程序列表。以下這一行代碼就是用來(lái)為 .aspx 資源定義 HTTP 處理程序的。 <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/> 擴(kuò)展名可以與處理程序類相關(guān)聯(lián),并且更多是與處理程序工廠類相關(guān)聯(lián)。在所有情況下,負(fù)責(zé)處理請(qǐng)求的 HttpApplication 對(duì)象都會(huì)獲得一個(gè)實(shí)現(xiàn) IHttpHandler 接口的對(duì)象。如果根據(jù) HTTP 處理程序來(lái)解析關(guān)聯(lián)的資源/類,則返回的類將直接實(shí)現(xiàn)接口。如果資源被綁定到處理程序工廠,則還需要額外的步驟。處理程序工廠類實(shí)現(xiàn) IHttpHandlerFactory 接口,此接口的 GetHandler 方法將返回一個(gè)基于 IHttpHandler 的對(duì)象。 HTTP 運(yùn)行時(shí)是如何結(jié)束這個(gè)循環(huán)并處理頁(yè)面請(qǐng)求的?ProcessRequest 方法在 IHttpHandler 接口中非常重要。通過(guò)對(duì)代表被請(qǐng)求頁(yè)面的對(duì)象調(diào)用此方法,ASP.NET 結(jié)構(gòu)會(huì)啟動(dòng)將生成瀏覽器輸出的進(jìn)程。 真正的 Page 類 特定頁(yè)面的 HTTP 處理程序類型取決于 URL。首次調(diào)用 URL 時(shí),將構(gòu)建一個(gè)新的類,這個(gè)類被動(dòng)態(tài)編譯為一個(gè)程序集。檢查 .aspx 資源的分析進(jìn)程的結(jié)果是類的源代碼。該類被定義為命名空間 ASP 的組成部分,并且被賦予了一個(gè)模擬原始 URL 的名稱。例如,如果 URL 的終點(diǎn)是 page.aspx,則類的名稱就是 ASP.Page_aspx。不過(guò),類的名稱可以通過(guò)編程方式來(lái)控制,方法是在 @Page 指令中設(shè)置 ClassName 屬性。 HTTP 處理程序的基類是 Page。這個(gè)類定義了由所有頁(yè)面處理程序共享的方法和屬性的最小集合。Page 類實(shí)現(xiàn) IHttpHandler 接口。 在很多情況下,實(shí)際處理程序的基類并不是 Page,而是其他的類。例如,如果使用了代碼分離,就會(huì)出現(xiàn)這種情況。代碼分離是一項(xiàng)開(kāi)發(fā)技術(shù),它可以將頁(yè)面所需的代碼隔離到單獨(dú)的 C# 和 Microsoft Visual Basic? .NET 類中。頁(yè)面的代碼是一組事件處理程序和輔助方法,這些處理程序和方法真正決定了頁(yè)面的行為。可以使用 <script runat=server> 標(biāo)記對(duì)此代碼進(jìn)行內(nèi)聯(lián)定義,或者將其放置在外部類(代碼分離類)中。代碼分離類是從 Page 繼承并使用額外的方法的類,被指定用作 HTTP 處理程序的基類。 還有一種情況,HTTP 處理程序也不是基于 Page 的,即在應(yīng)用程序配置文件的 <pages> 部分中,包含了 PageBaseType 屬性的重新定義。 <pages PageBaseType="Classes.MyPage, mypage" /> PageBaseType 屬性指明包含頁(yè)面處理程序的基類的類型和程序集。從 Page 導(dǎo)出的這個(gè)類可以自動(dòng)賦予處理程序擴(kuò)展的自定義方法和屬性集。 頁(yè)面的生命周期 完全識(shí)別 HTTP 頁(yè)面處理程序類后,ASP.NET 運(yùn)行時(shí)將調(diào)用處理程序的 ProcessRequest 方法來(lái)處理請(qǐng)求。通常情況下,無(wú)需更改此方法的實(shí)現(xiàn),因?yàn)樗怯?Page 類提供的。 此實(shí)現(xiàn)將從調(diào)用為頁(yè)面構(gòu)建控件樹(shù)的 FrameworkInitialize 方法開(kāi)始。FrameworkInitialize 方法是 TemplateControl 類(Page 本身從此類導(dǎo)出)的一個(gè)受保護(hù)的虛擬成員。所有為 .aspx 資源動(dòng)態(tài)生成的處理程序都將覆蓋 FrameworkInitialize。在此方法中,構(gòu)建了頁(yè)面的整個(gè)控件樹(shù)。 接下來(lái),ProcessRequest 使頁(yè)面經(jīng)歷了各個(gè)階段:初始化、加載視圖狀態(tài)信息和回發(fā)數(shù)據(jù)、加載頁(yè)面的用戶代碼以及執(zhí)行回發(fā)服務(wù)器端事件。之后,頁(yè)面進(jìn)入顯示模式:收集更新的視圖狀態(tài),生成 HTML 代碼并隨后將代碼發(fā)送到輸出控制臺(tái)。最后,卸載頁(yè)面,并認(rèn)為請(qǐng)求處理完畢。 在各個(gè)階段中,頁(yè)面會(huì)觸發(fā)少數(shù)幾個(gè)事件,這些事件可以由 Web 控件和用戶定義的代碼截取并進(jìn)行處理。其中的一些事件是嵌入式控件專用的,因此無(wú)法在 .aspx 代碼級(jí)進(jìn)行處理。 要處理特定事件的頁(yè)面應(yīng)該明確注冊(cè)一個(gè)適合的處理程序。不過(guò),為了向后兼容早期的 Visual Basic 編程風(fēng)格,ASP.NET 也支持隱式事件掛鉤的形式。默認(rèn)情況下,頁(yè)面會(huì)嘗試將特定的方法名稱與事件相匹配,如果實(shí)現(xiàn)匹配,則認(rèn)為此方法就是匹配事件的處理程序。ASP.NET 提供了六種方法名稱的特定識(shí)別,它們是 Page_Init、Page_Load、Page_DataBind、Page_PreRender 和 Page_Unload。這些方法被認(rèn)為是由 Page 類提供的相應(yīng)事件的處理程序。HTTP 運(yùn)行時(shí)會(huì)自動(dòng)將這些方法綁定到頁(yè)面事件,這樣,開(kāi)發(fā)人員就不必再編寫(xiě)所需的粘接代碼了。例如,如果命名為 Page_Load 的方法綁定到頁(yè)面的 Load 事件,則可省去以下代碼。 this.Load += new EventHandler(this.Page_Load); 對(duì)特定名稱的自動(dòng)識(shí)別是由 @Page 指令的 AutoEventWireup 屬性控制的。如果該屬性設(shè)置為 false,則要處理事件的所有應(yīng)用程序都需要明確連接到頁(yè)面事件。不使用自動(dòng)綁定事件的頁(yè)面性能會(huì)稍好一些,因?yàn)椴恍枰~外匹配名稱與事件。請(qǐng)注意,所有 Microsoft Visual Studio? .NET 項(xiàng)目都是在禁用 AutoEventWireup 屬性的情況下創(chuàng)建的。但是,該屬性的默認(rèn)設(shè)置是 true,即 Page_Load 等方法會(huì)被識(shí)別,并被綁定到相關(guān)聯(lián)的事件。 下表中按順序列出了頁(yè)面的執(zhí)行包括的幾個(gè)階段,執(zhí)行的標(biāo)志是一些應(yīng)用程序級(jí)的事件和/或受保護(hù)并可覆蓋的方法。 表 1:ASP.NET 頁(yè)面生命中的關(guān)鍵事件
以上所列的階段中有些在頁(yè)面級(jí)是不可見(jiàn)的,并且僅對(duì)服務(wù)器控件的編寫(xiě)者和要?jiǎng)?chuàng)建從 Page 導(dǎo)出的類的開(kāi)發(fā)人員有意義。Init、Load、PreRender、Unload,再加上由嵌入式控件定義的所有回發(fā)事件,就構(gòu)成了向外發(fā)送頁(yè)面的各個(gè)階段標(biāo)記。 |
|