摘要了解為 ASPNET Web 頁面建立的事件模型以及 Web 頁面轉變為 HTML 過程中的各個階段ASPNET HTTP 運行時負責管理對象管道這些對象首先將請求的 URL 轉換成 Page 類的具體實例然後再將這些實例轉換成純 HTML 文本本文將探討那些作為頁面生命周期標志的事件以及控件和頁面編寫者如何干預並改變標准行為
簡介
對由 Microsoft? Internet 信息服務 (IIS) 處理的 Microsoft? ASPNET 頁面的每個請求都會被移交到 ASPNET HTTP 管道HTTP 管道由一系列托管對象組成這些托管對象按順序處理請求並將 URL 轉換為純 HTML 文本HTTP 管道的入口是 HttpRuntime 類ASPNET 結構為輔助進程中的每個 AppDomain 創建一個此類的實例(請注意輔助進程為每個當前正在運行的 ASPNET 應用程序維護一個特定的 AppDomain)
HttpRuntime 類從內部池中獲取 HttpApplication 對象並安排此對象來處理請求HTTP 應用程序管理器完成的主要任務就是找到將真正處理請求的類當請求 aspx 資源時處理程序就是頁面處理程序即從 Page 繼承的類的實例資源類型和處理程序類型之間的關聯關系存儲在應用程序的配置文件中更確切地說默認的映射集是在 machineconfig 文件的 <httpHandlers> 部分定義的但是應用程序可以在本地的 webconfig 文件中自定義自己的 HTTP 處理程序列表以下這一行代碼就是用來為 aspx 資源定義 HTTP 處理程序的
<add verb=* path=*aspx type=SystemWebUIPageHandlerFactory/>
擴展名可以與處理程序類相關聯並且更多是與處理程序工廠類相關聯在所有情況下負責處理請求的 HttpApplication 對象都會獲得一個實現 IHttpHandler 接口的對象如果根據 HTTP 處理程序來解析關聯的資源/類則返回的類將直接實現接口如果資源被綁定到處理程序工廠則還需要額外的步驟處理程序工廠類實現 IHttpHandlerFactory 接口此接口的 GetHandler 方法將返回一個基於 IHttpHandler 的對象
HTTP 運行時是如何結束這個循環並處理頁面請求的?ProcessRequest 方法在 IHttpHandler 接口中非常重要通過對代表被請求頁面的對象調用此方法ASPNET 結構會啟動將生成浏覽器輸出的進程
真正的 Page 類
特定頁面的 HTTP 處理程序類型取決於 URL首次調用 URL 時將構建一個新的類這個類被動態編譯為一個程序集檢查 aspx 資源的分析進程的結果是類的源代碼該類被定義為命名空間 ASP 的組成部分並且被賦予了一個模擬原始 URL 的名稱例如如果 URL 的終點是 pageaspx則類的名稱就是 ASPPage_aspx不過類的名稱可以通過編程方式來控制方法是在 @Page 指令中設置 ClassName 屬性
HTTP 處理程序的基類是 Page這個類定義了由所有頁面處理程序共享的方法和屬性的最小集合Page 類實現 IHttpHandler 接口
在很多情況下實際處理程序的基類並不是 Page而是其他的類例如如果使用了代碼分離就會出現這種情況代碼分離是一項開發技術它可以將頁面所需的代碼隔離到單獨的 C# 和 Microsoft Visual Basic? NET 類中頁面的代碼是一組事件處理程序和輔助方法這些處理程序和方法真正決定了頁面的行為可以使用 <script runat=server> 標記對此代碼進行內聯定義或者將其放置在外部類(代碼分離類)中代碼分離類是從 Page 繼承並使用額外的方法的類被指定用作 HTTP 處理程序的基類
還有一種情況HTTP 處理程序也不是基於 Page 的即在應用程序配置文件的 <pages> 部分中包含了 PageBaseType 屬性的重新定義
<pages PageBaseType=ClassesMyPage mypage />
PageBaseType 屬性指明包含頁面處理程序的基類的類型和程序集從 Page 導出的這個類可以自動賦予處理程序擴展的自定義方法和屬性集
頁面的生命周期
完全識別 HTTP 頁面處理程序類後ASPNET 運行時將調用處理程序的 ProcessRequest 方法來處理請求通常情況下無需更改此方法的實現因為它是由 Page 類提供的
此實現將從調用為頁面構建控件樹的 FrameworkInitialize 方法開始FrameworkInitialize 方法是 TemplateControl 類(Page 本身從此類導出)的一個受保護的虛擬成員所有為 aspx 資源動態生成的處理程序都將覆蓋 FrameworkInitialize在此方法中構建了頁面的整個控件樹
接下來ProcessRequest 使頁面經歷了各個階段初始化加載視圖狀態信息和回發數據加載頁面的用戶代碼以及執行回發服務器端事件之後頁面進入顯示模式收集更新的視圖狀態生成 HTML 代碼並隨後將代碼發送到輸出控制台最後卸載頁面並認為請求處理完畢
在各個階段中頁面會觸發少數幾個事件這些事件可以由 Web 控件和用戶定義的代碼截取並進行處理其中的一些事件是嵌入式控件專用的因此無法在 aspx 代碼級進行處理
要處理特定事件的頁面應該明確注冊一個適合的處理程序不過為了向後兼容早期的 Visual Basic 編程風格ASPNET 也支持隱式事件掛鉤的形式默認情況下頁面會嘗試將特定的方法名稱與事件相匹配如果實現匹配則認為此方法就是匹配事件的處理程序ASPNET 提供了六種方法名稱的特定識別它們是 Page_InitPage_LoadPage_DataBindPage_PreRender 和 Page_Unload這些方法被認為是由 Page 類提供的相應事件的處理程序HTTP 運行時會自動將這些方法綁定到頁面事件這樣開發人員就不必再編寫所需的粘接代碼了例如如果命名為 Page_Load 的方法綁定到頁面的 Load 事件則可省去以下代碼
thisLoad += new EventHandler(thisPage_Load);
對特定名稱的自動識別是由 @Page 指令的 AutoEventWireup 屬性控制的如果該屬性設置為 false則要處理事件的所有應用程序都需要明確連接到頁面事件不使用自動綁定事件的頁面性能會稍好一些因為不需要額外匹配名稱與事件請注意所有 Microsoft Visual Studio? NET 項目都是在禁用 AutoEventWireup 屬性的情況下創建的但是該屬性的默認設置是 true即 Page_Load 等方法會被識別並被綁定到相關聯的事件
下表中按順序列出了頁面的執行包括的幾個階段執行的標志是一些應用程序級的事件和/或受保護並可覆蓋的方法
表 ASPNET 頁面生命中的關鍵事件
以上所列的階段中有些在頁面級是不可見的並且僅對服務器控件的編寫者和要創建從 Page 導出的類的開發人員有意義InitLoadPreRenderUnload再加上由嵌入式控件定義的所有回發事件就構成了向外發送頁面的各個階段標記
執行的各個階段
頁面生命周期中的第一個階段是初始化這個階段的標志是 Init 事件在成功創建頁面的控件樹後將對應用程序觸發此事件換句話說當 Init 事件發生時aspx 源文件中靜態聲明的所有控件都已實例化並采用各自的默認值控件可以截取 Init 事件以初始化在傳入的 Web 請求的生命周期內所需的所有設置例如這時控件可以加載外部模板文件或設置事件的處理程序請注意這時視圖狀態信息尚不可用
初始化之後頁面框架將加載頁面的視圖狀態視圖狀態是名稱/值對的集合在此集合中控件和頁面本身存儲了對所有 Web 請求都必須始終有效的全部信息視圖狀態代表了頁面的調用上下文通常它包含上次在服務器上處理頁面時控件的狀態首次在會話中請求頁面時視圖狀態為空默認情況下視圖狀態存儲在靜默添加到頁面的隱藏字段中該字段的名稱是 __VIEWSTATE通過覆蓋 LoadViewState 方法(Control 類的受保護可覆蓋方法)組件開發人員可以控制視圖狀態的存儲方式以及視圖狀態的內容映射到內部狀態的方式
有些方法(如 LoadPageStateFromPersistenceMedium 以及其對應的 SavePageStateToPersistenceMedium)可以用來將視圖狀態加載並保存到其他存儲介質(例如會話數據庫或服務器端文件)中與 LoadViewState 不同上述方法只能在從 Page 導出的類中使用
存儲視圖狀態之後頁面樹中控件的狀態與頁面最後一次顯示在浏覽器中的狀態相同下一步是更新它們的狀態以加入客戶端的更改處理回發數據階段使控件有機會更新其狀態從而准確反映客戶端相應的 HTML 元素的狀態例如服務器的 TextBox 控件對應的 HTML 元素是 <input type=text>在回發數據階段TextBox 控件將檢索 <input> 標記的當前值並使用該值來刷新自己內部的狀態每個控件都要從回發的數據中提取值並更新自己的部分屬性TextBox 控件將更新它的 Text 屬性而 CheckBox 控件將刷新它的 Checked 屬性服務器控件和 HTML 元素的對應關系可以通過二者的 ID 找到
在處理回發數據階段的最後頁面中的所有控件的狀態都將使用客戶端輸入的更改來更新前一狀態這時將對頁面觸發 Load 事件
頁面中可能會有一些控件當其某個敏感屬性在兩個不同的請求中被修改時需要完成特定的任務例如如果 TextBox 控件的文本在客戶端被修改則此控件將觸發 TextChanged 事件每個控件在其一個或多個屬性被修改為客戶端輸入的值時都可以決定觸發相應的事件對於這些更改對其非常關鍵的控件控件實現 IPostBackDataHandler 接口此接口的 LoadPostData 方法是在 Load 事件後立即調用的通過對 LoadPostData 方法進行編碼控件將驗證自上次請求後是否發生了關鍵更改並觸發自己的更改事件
頁面生命周期中的關鍵事件是被調用以執行服務器端代碼的事件此代碼與客戶端觸發的事件相關聯當用戶單擊按鈕時將回發頁面回發值的集合中包括啟動整個操作的按鈕的 ID如果控件實現 IPostBackEventHandler 接口(如按鈕和鏈接按鈕)頁面框架將調用 RaisePostBackEvent 方法此方法的行為取決於控件的類型就按鈕和鏈接按鈕而言此方法將查找 Click 事件處理程序並運行相關的委托
處理完回發事件之後頁面就可以顯示了這個階段的標志是 PreRender 事件控件可以利用這段時間來執行那些需要在保存視圖狀態和顯示輸出的前一刻執行的更新操作下一個狀態是 SaveViewState在此狀態中所有控件和頁面本身都將更新自己 ViewState 集合的內容然後將得到序列化散列Base 編碼的視圖狀態而且此視圖狀態與隱藏字段 __VIEWSTATE 相關聯
通過覆蓋 Render 方法可以改變各個控件的顯示機制此方法接受 HTML 書寫器對象並使用此對象來積累所有要為控件生成的 HTML 文本Page 類的 Render 方法的默認實現包括對所有成員控件的遞歸調用對於每個控件頁面都將調用 Render 方法並緩存 HTML 輸出
頁面生命中的最後一個標志是 Unload 事件在頁面對象消除之前發生在此事件中您應該釋放所有可能占用的關鍵資源(例如文件圖形對象數據庫連接等)
在此事件之後也就是最後浏覽器接收 HTTP 響應數據包並顯示頁面
小結
ASPNET 頁面對象模型因其事件機制而顯得格外新穎獨特Web 頁面由控件組成這些控件既可以產生豐富的基於 HTML 的用戶界面又可以通過事件與用戶交互以前在 Web 應用程序的上下文中設置事件模型是件有挑戰性的工作可我們驚奇的看到客戶端生成的事件可以由服務器端的代碼來解決而且只進行一些相應的修改後此過程仍可以輸出相同的 HTML 頁面
掌握這個模型對於了解頁面生命周期的各個階段以及頁面對象如何被 HTTP 運行時實例化並使用是非常重要的
From:http://tw.wingwit.com/Article/program/net/201311/15740.html