當 HTTP 請求抵達伺服器時,ASP.NET 會啟動一套精密的處理管線來回應。這一切始於 ISAPIRuntime.ProcessRequest() 方法,它隨即呼叫 HttpRuntime.ProcessRequest 內部方法(可透過 Reflector 觀察 HttpRuntime.ProcessRequestInternal),執行幾項關鍵動作:
HttpContext 實例,封裝請求上下文。HttpApplication 實例。HttpApplication.Init() 設定管線事件。HttpApplication.ResumeProcessing() 正式啟動管線處理。一個新的 HttpContext 物件會在管線伊始被建立,並包裝 ISAPIWorkerRequest(即 ISAPI ECB 的 managed 包裝器)。此上下文物件貫穿整個請求生命週期,隨時可透過靜態屬性 HttpContext.Current 存取。正如其名,HttpContext 代表當前活動請求的上下文,因為它聚合了請求處理期間所有典型需要的重要物件:Request、Response、Application、Server、Cache。在請求處理的任何時刻,HttpContext.Current 都能讓你存取這些物件。
HttpContext 還提供一個極實用的 Items 集合,可用於保存特定請求的資料。上下文物件在請求開始時建立、結束時釋放,因此 Items 中的資料僅在該請求範圍內有效。典型應用如請求日誌機制:在 Global.asax 掛接 Application_BeginRequest 與 Application_EndRequest,記錄請求起迄時間(見下列程式碼片段)。若在請求或頁面處理的不同部分需共享資料,HttpContext.Items 便是理想選擇。
protected void Application_BeginRequest(Object sender, EventArgs e)
{
//*** Request Logging
if (App.Configuration.LogWebRequests)
Context.Items.Add("WebLog_StartTime", DateTime.Now);
}
protected void Application_EndRequest(Object sender, EventArgs e)
{
// *** Request Logging
if (App.Configuration.LogWebRequests)
{
try
{
TimeSpan Span = DateTime.Now.Subtract((DateTime)Context.Items["WebLog_StartTime"]);
int MiliSecs = (int)Span.TotalMilliseconds;
// do your logging
WebRequestLog.Log(App.Configuration.ConnectionString, true, MiliSecs);
}
catch { }
}
}
上下文就緒後,ASP.NET 需透過 HttpApplication 將請求路由至適當的應用程式/虛擬目錄。每個 ASP.NET 應用程式必須對應一個虛擬目錄(或 Web 根目錄),且每個「應用程式」皆獨立處理。HttpApplication 宛如儀式的主人——處理動作的起點。
每個請求都會被分派給一個 HttpApplication 物件。HttpApplicationFactory 類別依應用程式負載建立一組 HttpApplication 物件池,並為每個請求分配引用。物件池大小受 machine.config 中 processModel 區段的 maxWorkerThreads 限制,預設為 20(註:經反編譯驗證,池大小實際上限為 100;若池小於 100,工廠會補足至 100,但多執行緒並發建立時可能略超過 100)。
