ASP.NET 請求處理流程
HTTP處理流程圖
以上流程的一些概念解釋:
1.http.sys
是一個位于Win2003和WinXP SP2中的操作系統核心組件,能夠讓任何應用程序通過它提供的接口,以http協議進行信息通訊。
溫馨提示:如果用戶不慎刪除了該驅動文件,不用擔心,該驅動會在下次系統啟動時重建。是一個刪不掉的系統核心組件!實用程序結束該驅動,該驅動也會馬上重新創建(只有粉碎文件才不能馬上重建,但粉碎后,下次啟動會重建)。
微軟在Windows 2003 Server里引進了新的HTTP API和kernel mode driver Http.sys,目的是使基于Http服務的程序更有效率。這個改變的直接收益者就是IIS 6.0和ASP.NET。
其實在Windows XP安裝SP2后,Http.sys已經出現在系統里了,但事實上,操作系統并沒有真的使用這個內核級驅動,而XP上自帶的IIS 5.1也沒有使用HTTP API。
新的HTTP API里最核心的變化都封裝在Http.sys這個kernel mode driver里了。在此之前,基于HTTP協議的程序都是在User mode下運行的,而且必須自己處理諸如軟件中斷、context switch、線程調度等等問題,并且往往無法自由接觸系統資源。過去,HTTP服務器,如IIS, Apache等都是利用Winsock API來創建一個User mode下的network listener。Network listener通常獨自(i.e.: per application or per thread basis)占用一個IP端口。通俗點說,就是在同一時間只有一個應用程序可以監聽一個端口,這在有些時候是一個不太令人舒服的限制。
新的Http.sys帶來的好處大致有如下一些:
1. 緩存 - 靜態的內容現在被緩存于內核模式下,這使服務響應速度更快
2. 記錄 (Log)-IIS的log功能更快且標準化了
3. 帶寬控制 - greater scalability control and throttling
4. 可靠性 - 所有的服務請求會在Http.sys里暫存入隊列,而不是由服務程序本身來處理,這樣,即使服務程序重啟,尚未被處理的請求也不會丟失了
5. IP端口重用 - 現在,只要是通過Http.sys管理的端口(基本包括了那些著名的端口,比如80),都可以同時允許多個程序同時監聽了。
2.應用程序池
應用池即應用程序池:
在Microsoft IIS6中引入了應用程序池,這是微軟的一個全新概念:應用程序池是將一個或多個應用程序鏈接到一個或多個工作進程集合的配置。因為應用程序池中的應用程序與其他應用程序被工作進程邊界分隔,所以某個應用程序池中的應用程序不會受到其他應用程序池中應用程序所產生的問題的影響。 Windows 2003同時支持兩種工作模式,默認為ISS 6.0工作進程隔離模式。工作進程隔離模式防止一個應用程序或站點停止了而影響另一個應用程序或站點,大大增強了IIS的可靠性。那么如何設置兩種工作模式呢?
啟動IIS管理器,右擊網站,選擇“屬性”,打開屬性對話框。在IIS 6.0工作進程隔離模式下,所有的應用程序代碼都在隔離環境中運行,它們是如何進行隔離的呢?Windows 2003新增了應用程序池,工作進程隔離模式允許客戶創建多個應用程序池,每個應用程序池都可以有不同的配置。因為這些應用程序池直接從內核(而非WWW 服務)接收它們的請求,所以性能和可靠性得到了增強。要隔離運行在同一臺計算機上但屬于不同網站的Web應用程序,需要為每個網站創建單獨的應用程序池。
創建應用程序池
在IIS管理器中,打開本地計算機,右鍵單擊“應用程序池”,選擇新建“應用程序池” (必須在工作進程隔離模式下才能建立應用程序池) 。“應用程序池名稱”框中,輸入新的應用程序池名稱。如果在“應用程序池 ID”框中出現的 ID (如:AppPool #1)不是您想要的,可進行重命名。如果您單擊了“將現有應用程序池作為模板”,請在“應用程序池名稱”框中右鍵單擊想要用來作為模板的應用程序池。最后單擊[確定]。
指派應用程序池
在 IIS 管理器中,右鍵單擊您要為其指派應用程序池的應用程序,然后單擊“屬性”。 單擊“主目錄”選項卡,確認您正在指派的目錄或虛擬目錄的“應用程序名”是否已被填寫。如果“應用程序名”框尚未被填寫,請單擊“創建”,然后輸入名稱。
在“應用程序池”列表框中,選擇您想要為其指派的應用程序池的名稱。最后單擊[確定]。
3.aspnet_isapi.dll
這個文件是asp.net的處理文件,用于處理asp.net在iis中注冊過擴展名的文件比如默認的.aspx文件注冊到aspnet_isapi.dll,這樣用戶訪問.aspx文件時,服務器自動調用aspnet_isapi.dll來處理這個文件.
這個DLL是asp.net底層的一個類庫,這是它的介紹: 當客戶端向web服務器請求一個*.aspx的頁面文件時,同asp類似,這個http請求也會被inetinfo.exe進程截獲(www服務),它判斷文件后綴之后,把這個請求轉交給ASPNET_ISAPI.DLL而ASPNET_ISAPI.DLL則會通過一個Http PipeLine的管道,將這個http請求發送給ASPNET_WP.EXE進程,當這個HTTP請求進入ASPNET_WP.EXE進程之后,asp.net framework就會通過HttpRuntime來處理這個Http請求,處理完畢后將結果返回給客戶端。
4.CLR
http://developer.51cto.com/art/200910/158022.htm
http://developer.51cto.com/art/200909/152551.htm
5.AppDomain
http://www.cnblogs.com/chorrysky/archive/2008/07/16/1244089.html
6.ISAPIRuntime
管理 .NET Framework 中的 HttpWorkerRequest (此抽象類定義由 ASP.NET 托管代碼用于處理請求的基本輔助方法和枚舉)對象。
-----------------------------------------------------------------------------------------------------------------------------
當第一個請求到達時的動作如下圖所示:
初始化所有核心應用程序對象之后,將通過創建 HttpApplication 類的實例啟動應用程序。如果應用程序具有 Global.asax 文件,則 ASP.NET 會創建 Global.asax 類(從 HttpApplication 類派生)的一個實例,并使用該派生類表示應用程序。
每次http請求都會有一個HttpApplication類型的對象來管理這次請求的過程。
為每次http請求創建一個HttpContext對象,此對象在整個Http處理過程中都是可訪問的。
HttpApplication對象負責裝配出整個“HTTP請求處理管線(HTTP Pipeline)”,這相當于一條加工流水線,讓HttpContext對象在這條線上過一遍。
HttpContext對象經過流水線的不同部分時,HttpApplication對象會先后激發出一連串的事件。HTTP模塊(HTTP Module)可以響應這些事件,在此事件響應代碼中可以對HttpContext對象進行“加工和處理”。
HTTP模塊可以看成是“生產流水線”中的工人。
HTTP模塊對象是在HttpApplication對象的InitModules()方法中被創建的,創建時會調用其init方法,我們一般在HTTP模塊對象Init()方法中書寫代碼對HttpApplication對象的事件進行注冊。
在流水線的中部(處理完了相關的事件),HttpContext對象被最終的Page對象所接收(這就是為何可以在ASP.NET頁面中通過Page類定義的Context屬性訪問HttpContext對象的原因)。
每個被訪問的ASP.NET頁面都會被轉換為一個“派生自Page類的頁面類”。
Page類實現了IHttpHandler接口,此接口定義了一個ProcessRequest()方法。
ASP.NET頁面類生成以后被自動編譯為程序集,然后其ProcessRequest()方法被自動調用。ProcessRequest()方法的執行結果再次被HttpContext對象所承載,控制又轉回到“HTTP請求處理流水線”中,HttpApplication對象繼續激發后繼的事件。這時,如果還有特定的HTTP模塊響應這些事件,則它們會被自動調用。
HttpContext對象帶著最后的處理結果來到了“HTTP請求處理管線”的未端,其信息被取出來,再次以aspnet_isapi.dll為橋梁傳送給工作者進程。工作者進程再將HTTP請求的處理結果轉給HTTP.SYS,由它負責將結果返回給瀏覽器。
我們所寫的那些頁面代碼,以及那一套復雜的頁面生命周期,都是整個asp.net周期中的一部分,所有IhttpHandler的操作,都是在Application的PreRequestHandlerExecute 事件和PostRequestHandlerExecute 事件之間完成的,IHttpHandler中的操作都是頁面級別的操作,他們不能跨出頁面這個范。
在應用程序級別的操作,應該使用IHttpModule。
要區分開HttpApplication和HttpApplicationState
HttpApplicationState是整個應用程序唯一的。
而HttpApplication則不是唯一的。
你在頁面中寫的Application[“key”] = “”value; 這個Application其實是HttpApplicationState
每一次請求都由一個HttpApplication對象來處理,在處理過程中,這個對象不能再處理其他請求,并發訪問時,會為不同訪問分配不同HttpApplication對象。
HttpApplicationFactory對象負責管理一個HttpApplication對象池,當有HTTP請求到來時,如果池中還有可用的 HttpApplication對象,則分配之,否則,創建一個新的HttpApplication對象。請求完畢后,HttpApplication會被回收以供再次使用。
每當新的HttpApplication對象創建時,都會根據配置創建新的Ihttpmodule對象,新的IHttpModule對象被創建時,其init方法就會被執行。
HttpApplication在不同處理階段觸發的事件列表
1. 對請求進行驗證,將檢查瀏覽器發送的信息,并確定其是否包含潛在惡意標記。有關更多信息,請參見 ValidateRequest 和腳本侵入概述。
2. 如果已在 Web.config 文件的 UrlMappingsSection 節中配置了任何 URL,則執行 URL 映射。
3. 引發 BeginRequest 事件。
4. 引發 AuthenticateRequest 事件。
5. 引發 PostAuthenticateRequest 事件。
6. 引發 AuthorizeRequest 事件。
7. 引發 PostAuthorizeRequest 事件。
8. 引發 ResolveRequestCache 事件。
9. 引發 PostResolveRequestCache 事件。
10. 根據所請求資源的文件擴展名(在應用程序的配置文件中映射),選擇實現 IHttpHandler 的類,對請求進行處理。如果該請求針對從 Page 類派生的對象(頁),并且需要對該頁進行編譯,則 ASP.NET 會在創建該頁的實例之前對其進行編譯。
11. 引發 PostMapRequestHandler 事件。
12. 引發 AcquireRequestState 事件。(加載初始化Session時候觸發)
13. 引發 PostAcquireRequestState 事件。
14. 引發 PreRequestHandlerExecute 事件。(在HTTP請求送入HttpHandler之前觸發)
15. 為該請求調用合適的 IHttpHandler 類的 ProcessRequest 方法(或異步版 BeginProcessRequest)。例如,如果該請求針對某頁,則當前的頁實例將處理該請求。
16. 引發 PostRequestHandlerExecute 事件。(在HTTP請求送入HttpHandler之后觸發)
17. 引發 ReleaseRequestState 事件。(存儲Session狀態的時候觸發)
18. 引發 PostReleaseRequestState 事件。
19. 如果定義了 Filter 屬性,則執行響應篩選。
20. 引發 UpdateRequestCache 事件。(更新緩存信息的時候觸發)
21. 引發 PostUpdateRequestCache 事件。
22. 引發 EndRequest 事件。
其他
通過使用命名約定 Application_event(如 Application_BeginRequest),ASP.NET 可在 Global.asax 文件中將應用程序事件自動綁定到處理程序。這與將 ASP.NET 頁方法自動綁定到事件(如頁的 Page_Load 事件)的方法類似。
如果文件擴展名尚未映射到 ASP.NET,則 ASP.NET 將不會接收該請求。對于使用 ASP.NET 身份驗證的應用程序,理解這一點非常重要。例如,由于 .htm 文件通常沒有映射到 ASP.NET,因此 ASP.NET 將不會對 .htm 文件請求執行身份驗證或授權檢查。因此,即使文件僅包含靜態內容,如果希望 ASP.NET 檢查身份驗證,也應使用映射到 ASP.NET 的文件擴展名創建該文件,如采用文件擴展名 .aspx。
如果要創建服務于特定文件擴展名的自定義處理程序,必須在 IIS 中將該擴展名映射到 ASP.NET,還必須在應用程序的 Web.config 文件中注冊該處理程序。有關更多信息,請參見 HTTP 處理程序介紹。
在AcquireRequestState事件(包含該事件) 到 PostRequestHandlerExecute事件(不包含該事件)之前這段時間里,Session都是可用的。Httphandler的ProcessRequest方法也是在這段時間內被調用的,所以用在頁面中可以用Session[“key”]來訪問Session。