學習ASP.NET MVC5架構揭秘筆記-IIS/ASP.NET管道(三),mvc5-iis
ASP.NET管道
以IIS6.0為例,它在背景工作處理序w3wp.exe中會利用aspnet_isapi.dll載入.NET運行時(如果.NET運行時尚未載入)。IIS6.0引入了應用程式進程池的概念,一個背景工作處理序對應著一個應用程式集區。一個應用程式進程池可以承載一個或者多個Web應用,每個Web應用映射到一個IIS虛擬目錄。與IIS5.x一樣,每一個Web應用運行在各自的應用程式定義域中。
如果HTTP.SYS接收到的HTTP請求是對該Web應用的第一次訪問,在成功載入運行時後,IIS會通過AppDomainFactory為該Web應用建立一個應用程式定義域。隨後一個特殊的運行時IsapiRuntime被載入。IsapiRuntime定義在程式集System.Web.dll中,對應的命名空間為“System.Web.Hosting”,被載入的IsapiRuntime會接管該HTTP請求。
接管HTTP請求的IsapiRuntime會首先建立一個IsapiWorkerRequest對象來封裝當前的HTTP請求,隨後將此對象傳遞給ASP.NET運行時HttpRuntime。從此時起,HTTP請求正式進入了ASP.NET管道。HttpRuntime會根據IsapiWorkerRequest對象建立用於表示當前HTTP請求的上下文(Context)對象HttpContext。
隨著HttpContext的建立,HttpRuntime會利用HttpApplicationFactory建立新的或擷取現有的HttpApplication對象。實際上ASP.NET維護著一個HttpApplication對象池,HttpApplicationFactory從池中選取可用的HttpApplication用於處理HTTP請求,處理完畢後將其釋放到對象池中。HttpApplication負責處理當前的HTTP請求。
在HttpApplication初始化過程中,ASP.NET會根據設定檔載入並初始化註冊的HttpModule對象。對於HttpApplication來說,在它處理HTTP請求的不同階段會觸發不同的事件(Event),而HttpModule的意義在於通過註冊HttpApplication的相應事件,將需要的操作注入整個HTTP請求的處理流程。ASP.NET的很多功能都是通過相應的HttpModule實現的。
最終完成對HTTP請求的處理實現在HttpHandler中,不同的資源類型對應著不同類型的HttpHandler。
1.HttpApplication
HttpApplication是整個ASP.NET基礎架構的核心,他負責處理分發給他的HTTP請求。由於一個HttpApplication對象在某個時刻只能處理一個請求,只有完成對某個請求的處理後才能用於後續請求的處理,所以ASP.NET採用對象池的機制來建立或擷取HttpApplication對象。
當第一個請求抵達時,ASP.NET會一次建立多個HttpApplication對象,並將其置於池中,然後選擇其中一個對象來處理請求。處理完畢後,HttpApplication對象不會被回收,而是釋放到池中。對於後續的請求,閒置HttpApplication對象會從池最中取出。如果池中所有的HttpApplication對象都處於繁忙狀態,在沒有超出HttpApplication池最大容量的情況下,ASP.NET會建立新的HttpApplication對象,否則將請求放入隊列等待現有HttpApplication的釋放。
HttpApplication處理請求的整個生命週期是一個相對複雜的過程,在該過程的不同階段會觸發相應的事件。我們可以註冊相應的事件,將處理邏輯注入到HttpApplication處理請求的某個階段。下表按照實現的先後順序列出了HttpApplication在處理每一個請求時觸發的事件名稱。
HttpApplication事件列表
名稱 |
描述 |
BeginRequest |
HTTP管道開始處理請求時,會觸發BeginRequest事件。 |
AuthenticateRuest, PostAuthenticateRequest |
ASP.NET先後觸發這兩個事件,使安全模組對請求進行身分識別驗證。 |
AuthorizeRequest, PostAuthorizeRequest |
ASP.NET先後觸發這兩個事件,使安全模組對請求進行授權。 |
ResolveRequestCache, PostResolveRequestCache |
ASP.NET先後觸發這兩個事件,以使緩衝模組利用緩衝的內容對請求直接進行響應(緩衝模組可以將響應內容進行緩衝,對於後續的請求,直接將緩衝的內容返回,從而提高響應能力) |
PostMapRequestHandler |
對於訪問不同的資源類型,ASP.NET具有不同的HttpHandler對其進行處理。對於每個請求,ASP.NET會通過副檔名選擇匹配相應的HttpHandler類型,匹配成功後,該事件觸發 |
AcquireRequestState, PostAcquireRequestState |
ASP.NET先後觸發這兩個事件,使狀態管理模組擷取基於當前請求相應的狀態,如SessionState |
PreRequestHandlerExecute, PostPreRequestHandlerExecute |
ASP.NET最終通過與請求資源相對應的HttpHandler實現對請求的處理,在實行HttpHandler前後,這兩個事件被先後觸發 |
ReleaseRequestState, PostReleaseRequestState |
ASP.NET先後觸發這兩個事件,使狀態管理模組釋放基於當前請求相應的狀態 |
UpdateRequestCache, PostUpdateRequestCache |
ASP.NET先後觸發這兩個事件,以使緩衝模組將HttpHandler處理請求得到的內容得以儲存到輸出緩衝中。 |
LogRequest, PostLogRequest |
ASP.NET先後觸發這兩個事件為當前請求進行日誌記錄 |
EndRequest |
整個請求處理完成之後,EndRequest事件被觸發 |
對於一個ASP.NET應用來說,HTTPApplication派生於Global.asax檔案,我們可以通過建立Global.asax檔案對HttpApplication的請求處理行為進行定製。Global.asax採用一種很直接的方式實現了這樣的功能,這種方式不是我們常用的方法重寫或事件註冊,而是直接採用方法名匹配。在Global.asax中。我們按照”Application_{Event Name}”這樣的方法命名規則進行事件註冊,比如Application_BeginRequestf方法處理HttpApplication的BeginRequest事件。
2.HttpModule
ASP.NET擁有一個具有高度可擴充性的引擎,並且能夠處理對於不同資源類型的請求。HttpModule功不可沒。
當請求轉入ASP.NET管道時,最終負責處理該請求的是與請求資源類型相匹配的HttpHandler對象,但是在HttpHandler正式工作之前ASP.NET會先載入並初始化所有配置的HttpModule對象。HttpModule在初始化過程中,會將一些回調操作註冊到HttpApplication相應的事件中,在HttpApplication請求處理生命週期中的某個階段,相應的事件會被觸發,通過HttpModule註冊的事件處理常式也得以執行。
所有的HttpModule都實現了具有如下定義的System.Web.IHttpModule介面,其Init方法實現了針對自身的初始化。該方法接收一個HttpApplication對象,有了這個對象,事件註冊就很容易了。
public interface IHttpModule{void Dispose();void Init(HttpApplication context);}
ASP.NET提供的很多基礎功能都是通過相應的HttpModule實現的,下面列出了一些典型的HttpModule,除了這些系統定義的HttpModule之外,我們還可以自訂HttpModule,通過Web.config可以很容易地將其註冊到Web應用中。
(1)OutputCacheModule:實現了輸出緩衝的功能。
(2)SessionStateModule:在無狀態的HTTP協議上實現了基於會話的狀態保持。
(3)WindowAuthenticationModule + FormsAuthenticationModule + PassportAuthenticationModule:實現了Window、Forms和Passport這三種典型的身份認證方式。
(4)UrlAuthorizationModule + FileAuthorizationModule:實現了基於URI和檔案ACL的授權。
3.HttpHandler
對於不同資源類型的請求,ASP.NET會載入不同的Handler來處理。所有的HttpHandler都實現了具有如下定義的介面System.Web.IHttpHandler,定義其中的方法ProcessRequest提供了處理請求的實現。另一個非同步版本的HttpHandler的IHttpAsyncHandler介面繼承自IHttpHandler,它通過調用BeginProcessRequest/EndProcessRequest方法以非同步方式處理請求。
public interface IHttpHandler{void ProcessRequest(HttpContext context);bool IsReusable{get;}}public interface IHttpAsyncHandler : IHttpHandler{IAsyncRequest BeginProcessRequest(HttpContext context,AsyncCallback ch,object extraData);void EndProcessRequest(IAsyncRequest result);}
某些HttpHandler具有一個與之相關的HttpHandlerFactory,定義其中的方法GetHandler用於建立新的HttpHandler或者擷取已經存在的HttpHandler。
public interface IHttpHandlerFactory{IHttpHandler GetHandler(HttpContext context,String requestType,string url,string pathTranslated);void ReleaseHandler(IHttpHandler handler);}
HttpHandler和HttpHandlerFactory的類型都是配置在Web.config來使用的。
除了通過配置建立起HttpHandler類型與請求路徑之間的映射關係之外,我們還可以調用當前HttpContext具有如下定義的RemapHandler方法將一個HttpHandler對象映射到當前HTTP請求。如果不曾通過調用該方法進行HttpHandler的顯示映射,或者調用該方法時傳入的參數為Null,真正的HttpHandler對象的映射發生在HttpApplication的PostMapRequestHandler觸發之前,預設進行HttpHandler的依據就是Web.config的配置。
public sealed class HttpContext{public void RemapHandler(IHttpHandler handler)}
換句話說,在調用當前HttpContext的RemapHandler方法時指定一個具體的HttpHandler對象,是為了讓ASP.NET直接跳過預設的HttpHandler映射操作。由於預設的HttpHandler映射發生在HttpApplication的PostMapRequestHandler事件觸發之前,所以只有在這之前調用RemapHandler方法才有意義。