ASP.NET 頁面處理 (一)
一, Web服務(wù)器
在windows平臺中, ASP.NET由IIS提供支持, IIS由一個叫 inetinfo.exe 的可執(zhí)行文件表示, 它是一個非托管Win32程序, 且并不是一個CLR窯主. IIS通過提供的ISAPI擴(kuò)展技術(shù)使得開發(fā)者可以開發(fā)稱為ISAPI擴(kuò)展和ISAPI篩選器的組件來擴(kuò)充Web服務(wù)器的功能, 即處理對自定義資源的請求. 它們是一些win32 DLL, 通過在Web服務(wù)器的配置, 可以將特定資源和你的ISAPI DLL聯(lián)系起來. 事實(shí)上除了少數(shù)的資源如htm文件或圖片文件之外,大多數(shù)資源都由IIS交給了相應(yīng)的ISAPI DLL來處理. 二, ASP.NET進(jìn)程模型
完全由ISAPI擴(kuò)展來處理的Web應(yīng)用程序都是和Web服務(wù)器在同一個進(jìn)程中工作的, 這樣實(shí)現(xiàn)了性能的最優(yōu)化,然而從可靠性的角度分析, web服務(wù)器和應(yīng)用程序應(yīng)該是分離的, 它們應(yīng)該工作在不同的進(jìn)程中, 但這樣一來使得性能受到影響. ASP.NET同時考慮了性能和可靠性, 它使用分離的進(jìn)程模型, 包含了兩個元素: 一個在進(jìn)程內(nèi)作為一個連接器, 控制器, 另一個作為進(jìn)程外的輔助進(jìn)程, 或說工作者進(jìn)程. 這提供了一個靈活的機(jī)制, 使得應(yīng)用程序和工作者進(jìn)程分離, 即提供服務(wù)的工作者進(jìn)程可隨時中止, 卻不會影響應(yīng)用程序的壽命. 三, ASP.NET運(yùn)行時構(gòu)成
ASP.NET的ISAPI DLL是 aspnet_isapi.dll , 在web服務(wù)器中, 所有的ASP.NET資源都指向這個文件, 它是一個非托管程序, 也不是CLR宿主, 它和IIS工作在同一進(jìn)程中, 負(fù)責(zé)接收請求并對其進(jìn)行分派, 并且它會創(chuàng)建工作者進(jìn)程, 對于ASP.NET來說, 這個輔助進(jìn)程是 aspnet_wp.exe, 除此之外 aspnet_isapi.dll 還負(fù)責(zé)監(jiān)控工作者進(jìn)程的執(zhí)行情況, 可以在性能降低的時候終止它. 工作者進(jìn)程寄宿了CLR, 它用IIS中的虛擬目錄來區(qū)分應(yīng)用程序, 并為每一個對新應(yīng)用程序的請求創(chuàng)建一個AppDomain, 每一個新應(yīng)用程序請求都將導(dǎo)致產(chǎn)生一個新的AppDomain, 這些AppDomain都在同一個工作者進(jìn)程中, 而對一個正在運(yùn)行的應(yīng)用程序的請求僅會被創(chuàng)建一個新的應(yīng)用程序?qū)嵗? 出于性能考慮, aspnet_isapi.dll 使用異步方式將請求轉(zhuǎn)發(fā)給工作者進(jìn)程, 而工作者進(jìn)程必須保持請求被處理的順序, 并且只有在ISAPI中的內(nèi)部請求表中改變了當(dāng)前請求的狀態(tài)后, 該請求才被工作者進(jìn)程處理, 所以, 反方向的通訊是同步進(jìn)行的. 除了aspnet_isapi.dll 和 aspnet_wp.exe 這兩個重要的組成部分以外, ASP.NET還有其它一些組件: * aspnet_filter.dll 它是一個 ISAPI filter 用來備份ASP.NET的無Cookie會話狀態(tài).
* aspnet_state.exe 是一個windows服務(wù), 可以在本地或遠(yuǎn)程運(yùn)行, 它用來在web應(yīng)用程序的內(nèi)存空間之外保存 會話信息, 該服務(wù)啟動后, 可在應(yīng)用程序的配置文件中設(shè)置, 使所有會話信息保存在此進(jìn)程的內(nèi)存中. 四, ASP.NET基本處理過程
當(dāng)請求到達(dá)IIS時, 它會判斷資源類型, 如果是ASP.NET資源, 則ASP.NET ISAPI啟動工作者進(jìn)程, 并將請求排隊, 然后分配給工作者進(jìn)程, 接著輔助進(jìn)程會回復(fù)ISAPI它將處理該請求, 則該請求在 aspnet_isapi.dll 內(nèi)部狀態(tài)表中標(biāo)記為"executing", 輔助進(jìn)程開始處理, 執(zhí)行中途輔助進(jìn)程可能會需要服務(wù)器變量并與ISAPI通信. 完成處理后, 響應(yīng)被發(fā)送回 aspnet_isapi.dll, 則該請求的狀態(tài)被改為"Done", 接著被從請求表中刪除. ASP.NET 頁面處理 (二)
五, 輔助進(jìn)程中的請求處理 所有的對ASP.NET資源的請求實(shí)際上是通過ASP.NET HTTP管道來處理的, 輔助進(jìn)程的一個重要工作就是將請求交給HTTP管道, 那么所謂有HTTP管道是指一系列的托管對象, 它們依次對請求進(jìn)行處理, 處理完畢后, 應(yīng)答將會從原路返回. 這些對象由多個稱為 HttpModules 的對象和一個最終處理請求的 HttpHandler 對象組成, 請求先經(jīng)過所有的 HttpModules, 最終到達(dá) HttpHandler, 處理完成后, 應(yīng)答再次經(jīng)過這些 HttpModules回到客戶端. 六, HttpHandlers 和 HttpModules
HttpHandlers 實(shí)際就是實(shí)現(xiàn)了 IHttpHandler 接口的類, 我們可以通過實(shí)現(xiàn)這個接口來處理自定義的資源,在系統(tǒng)的 machine.config 文件中, 可以在 <httpHandlers> 小節(jié)找到特定擴(kuò)展名的資源相對應(yīng)的處理類之間的對應(yīng)關(guān)系, 當(dāng)然我們可以在web.config中改寫這個映射. 在我們的類中, 通過改寫ProcessRequest方法來自定 義請求處理. HttpModules 實(shí)際上就是實(shí)現(xiàn)了 IHttpModules 接口的類, 實(shí)現(xiàn)該接口的類通過注冊特定的HttpApplication類中的事件來將自己插入到Http管道中, 在請求被處理之前及處理完成后都會引發(fā)特定的事件, 導(dǎo)致注冊了這些事件的HttpModules的方法被調(diào)用. 一個實(shí)現(xiàn)了 IHttpModules 接口的類可在重寫的 Init 方法中注冊事件. 同樣HttpModules也必須在machine.config 或 web.config 中注冊, 在<httpModules> 小節(jié)中我們可以找到系統(tǒng)預(yù)定義的一些 HttpModules. 七, 進(jìn)入HTTP管道 (pipeline)
HttpRuntime是HTTP管道的入口, 每當(dāng)請求一個新的應(yīng)用程序, 就會有一個HttpRuntime的對象生成, 也就是說, 每一個AppDomain都有一個自己的HttpRuntime對象. 對象產(chǎn)生以后, 將調(diào)用它的 ProcessRequest方法. HttpRuntime的對象會初始化一系列有關(guān)請求處理的對象, 如 HttpContext 等. 然后會向 HttpApplicationFactory 請求一個 HttpApplication 對象. 應(yīng)用程序類是定義了所有應(yīng)用程序通用的方法和屬性, 事件. 它是在 Global.asax 文件背后的類, 這個文件包含對HttpApplication類事件的響應(yīng)代碼, 如果你使用代碼后置, 那么在編譯時會將動態(tài)生成一個叫Global的繼承自HttpApplication的類, 并且這個類和你的代碼后置類放在同一個程序集中.應(yīng)用程序工廠類維護(hù)了一個 HttpApplication 的對象池, 接收到請求后, 它根據(jù)URL來獲得虛擬目錄的信息, 并用它來和已有的HttpApplication匹配, 它嘗試獲取應(yīng)用程序文件 Global.asax 并編譯這個類, 然后在生成 HttpApplcation實(shí)例時觸發(fā) Application_OnStart 事件. 如果實(shí)例池中沒有可用的對象, 將會創(chuàng)建一個新的實(shí)例. HttpApplication 對象會讓所有注冊過的 HttpModules 對請求進(jìn)行預(yù)處理, 然后再去查找 HttpHandler. 八, 頁面處理類
HttpApplication 對象的一個任務(wù)就是找到針對特定請求資源的 HttpHandler, 它通過在 machine.config 或web.config 配置文件中根據(jù)擴(kuò)展名找到對應(yīng)的類, 如對于 .aspx 文件, 是由 PageHandlerFactory類來處理, 事實(shí)上, 在ASP.NET中大量使用了廠設(shè)計模式, 使我們通過間接的方式來獲得同一繼承體系的相關(guān)對象. PageHandlerFactory調(diào)用 GetHandler 方法來返回合適的對象. 在此之前, 它會嘗試找到代表廖頁面的程序集, 如果沒找到, 則編譯它, 然后再返回相應(yīng)的 HttpHandler 對象. 如果我們沒有代碼后置, 那么這個頁面動態(tài)編譯時會直接繼承自 Page, Page類將成為頁面處理程序的基類, 如果使用了代碼后置, 則在編譯時將靜態(tài)編譯這個后置的代碼為一個程序集, 注意這個代碼后置類也是繼承自 Page 的, 動態(tài)生成頁面的程序集的時候, 這個頁面類將繼承自代碼后置類, 那么頁面處理程序的基類將變成代碼后置類而不是 Page. 有一點(diǎn)注意的是 Page 類實(shí)現(xiàn)了 iHttpHandler 接口. 除此之外, 在web.config中的<pages>部分用PageBaseType可以指定頁面處理程序的基類. ASP.NET頁面處理(三)
九, 頁面初始化
找到了最后的 HttpHandler 以后, 這個處理類會執(zhí)行它的 ProcessRequest 方法對頁面進(jìn)行處理, 這個方法是由 Page 類提供的. 它首先調(diào)用 rameworkInitialize 方法來初始化控件樹, 這個方法是由 TemplateControl 類提供, 它是 Page 類的基類, 這個受保護(hù)的虛函數(shù)被所有動態(tài)生成的頁面類重寫, 頁面類調(diào)用自己的 __BuildControlTree方法初始化所有的控件. 接下來頁面會經(jīng)歷它生命周期的幾個階段, 中間還會觸發(fā)一些事件. 十, 自動處理的事件
為了兼容早期的VB的風(fēng)格, 在ASP.NET中支持隱式事件綁定, 即不需要顯式注冊事件, 也可以使事件處理方法得到回調(diào), 條件是必須使用特定的方法名來匹配, 這些方法是: * Page_Init
* Page_Load * Page_DataBind * Page_PreRender * Page_Unload 那么對這些方法堍的識別由一個屬性來控制, 這個屬性是Page指令的 AutoEventWireup, 如果為 false, 則需要顯式注冊事件, 默認(rèn)是 true.
十一, 頁面生命周期
頁面生命周期的第一個階段是初始化, 在 FrameworkInitialize 方法結(jié)束后, 會觸發(fā) Init 事件, 順便提一下, 如果啟用了頁面跟蹤, 即Page指令的 Trace 屬性為 true 的話, 在 ProcessRequest 中還會打出相應(yīng)的信息. 初始化完成后, 將會調(diào)用 LoadPageViewState, 這個方法加載頁面視圖狀態(tài), 實(shí)際上, 它會遞歸地調(diào)用所有控件的 LoadViewState 方法使得每個控件會得到自己的視圖狀態(tài), 這個數(shù)據(jù)是從頁面的 __ViewState隱藏字段提交上來的, 有一點(diǎn)要注意的是, 只有 IsPostBack 為 true 的時候, 才會調(diào)用這個方法, 而頁面第一次被請求的時候是沒有這一步的. 每個控件從視圖狀態(tài)中取回屬于自己的信息, 并將自己恢復(fù)為上次調(diào)用時的狀態(tài),
接下來, 控件會用新的提交數(shù)據(jù)來進(jìn)行更新, 緊接著, 頁面觸發(fā) load 事件, 有一些控件會要求在關(guān)鍵值被用戶更改后做出反應(yīng), 進(jìn)行相應(yīng)的處理, 那么它們會對檢查自己的屬性是否被更改, 并觸發(fā)相應(yīng)的事件. 這些控件實(shí)現(xiàn)了 IPostBackDataHandler 接口, 這個接口有一個 LoadPostData 方法, 實(shí)現(xiàn)了此接口的控件可在這個方法中驗(yàn)證更改, 并引發(fā)事件. 這些控件有 CheckBox, ListBox 等, 當(dāng)發(fā)現(xiàn)更改時, 比如 TextBox, 會調(diào)用這個接口的另一個方法 RaisePostDataChangedEvent, 對于 TextBox 來說, 引發(fā) TextChanged 事件. 這個是在 load 事件之后進(jìn)行的. 如果控件希望處理回發(fā)事件, 它必須實(shí)現(xiàn) IPostBackEventHandler 接口, 如 Button, Calendar 等, 如果回發(fā)控件中包含這些控件, 頁面會調(diào)用 RaisePostBackEvent 方法, 對于Button來說, 會找相應(yīng)的 Click 事件并處理. 接下來, 頁面會觸發(fā) PreRender 事件, 在保存視圖狀態(tài)及顯示之前, 可以通過這個事件來做一些處理. 這一步完成之后, 將會調(diào)用 SavePageViewState 保存視圖狀態(tài), 它會使 Control 類遞歸地收集每個控件的鍵值對, 并保存起來. 現(xiàn)在頁面已經(jīng)準(zhǔn)備好可以顯示了, 頁面調(diào)用 RenderControl 方法, 它個方法產(chǎn)生一個 HtmlWriter 對象, 并將其傳給自己的 Render 方法, 它包括對所有成員控件的遞歸 Render 方法的調(diào)用, 并積累所有 Html. 在此之后, 頁面將引發(fā) Unload 事件, 可以在此事件中釋放一些資源, 最后, 應(yīng)答會再次通過 HttpModules 并回到客戶端. 本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/danielhf/archive/2004/07/15/42412.aspx
|
|