ASP.NET 上下文對象 ASP.NET 提供了一系列對象用來給當前請求,將要返回到客戶端的響應,以及 Web 應用本身提供上下文信息。間接的,這些上下文對象也可以用來回去核心 ASP.NET 框架特性。 上下文對象提供了應用,當前請求,與當前請求相關聯的響應的信息。也提供了對多數重要的 ASP.
ASP.NET 上下文對象
ASP.NET 提供了一系列對象用來給當前請求,將要返回到客戶端的響應,以及 Web 應用本身提供上下文信息。間接的,這些上下文對象也可以用來回去核心 ASP.NET 框架特性。
上下文對象提供了應用,當前請求,與當前請求相關聯的響應的信息。也提供了對多數重要的 ASP.NET 平臺服務的訪問,比如安全與狀態數據。我們可以在 MVC 框架的 controllers 和 views 中使用上下文對象來根據當前的請求或者應用狀態數據來調整我們應用的響應。在創建模塊或者處理器的時候,我們也會使用到這些對象。基於 ASP.NET 服務(比如移動設備偵測),MVC 框架使用上下文對象來處理請求。
理解 ASP.NET 上下文對象
上下文的核心類就是 System.Web.HttpContext。它在整個 ASP.NET 框架和 MVC 框架中都可見,並且它扮演了通向其他上下文對象和 ASP.NET 特性與服務的入口。事實上,在 ASP.NET 框架,HttpContext 類是很核心的,它當中的許多重要屬性在接下來都會講到。
表 1 - 最常使用的 HttpContext 成員
名稱 | 描述 |
Application | 返回 HttpApplicationState 對象,用來管理應用狀態數據。 |
ApplicationInstance | 返回與當前請求相關聯的 HttpApplication 對象。 |
Cache | 返回一個 Cache 對象用來緩存數據。 |
Current | (靜態)返回當前請求的 HttpContext 對象。 |
CurrentHanlder | 返回 IHttpHanlder 實例用來為當前請求生成內容。 |
IsDebuggingEnabled | 如果調試器附加到了 ASP.NET 應用中就會返回 true。我們可以使用這個來執行與調試相關的活動,但是,如果我們這麼做,切記要在部署之前沒有調試器的情況下測試完整。 |
Items | 返回一個集合可以用來在參與當前請求處理的組件之前傳遞狀態數據。 |
GetSection(name) | 獲取 Web.config 文件中指定的配置塊。 |
Request | 返回一個 HttpRequest 對象用來提供當前正在被處理的請求的詳細信息。 |
Response | 返回一個 HttpResponse 對象用來提供當前正在構建的並即將發送到瀏覽器的響應的詳細信息。 |
Session | 返回一個 HttpSession 對象用來提供對會話狀態的訪問。在 PostAcquireRequestState 應用事件觸發之前,這個屬性只會返回 null。 |
Server | 返回一個 HttpServerUtility 對象,當中包含了實用函數,最實用的就是請求處理器的執行。 |
Timestamp | 返回一個 DateTime 對象,當中包含了 HttpContext 對象創建時的時間。 |
Trace | 用來記錄診斷信息。 |
HttpContext 也定義了方法和屬性可以用來管理請求生命周期——比如 CurrentNotification 和 IsPostNotification 屬性。接下來將會介紹其他的上下文對象特性,包括由 HttpContext 定義的。
我們可以在全局應用類中通過 Context 屬性獲取到 HttpContext 類的實例。這個屬性的名稱並不是全局的。在 controller 類中或者一個視圖中,我們需要使用 HttpContext 屬性來獲取。如果這些方式都失敗了,那麼我們就可以使用靜態的 HttpContext.Current 來獲取與當前請求相關聯的 HttpContext 對象。
表 2 - 在不同的 ASP.NET/MVC 組件中獲取一個 HttpContext 實例
組件 | 技術 |
Controller | 使用 Controller 中定義的 HttpContext 屬性,它是 MVC 框架中 controllers 的基類。 |
View | 使用 WebViewPage 中定義的 Context 屬性,它是用來編譯 Razor 視圖的基類。 |
Global Application Class | 使用 HttpApplication 中定義的一個方便的屬性——Context。 |
Module | 當調用的時候會在 Init 方法中傳遞一個 HttpContext 對象,生命周期事件處理器被傳遞給一個 HttpApplication 對象,這個對象當中定義了一個 Context 屬性。 |
Handler | ProceeRequest 方法被調用的時候會傳遞一個 HttpContext 對象。 |
Universally | 我們總是可以通過 HttpContext.Current 屬性來獲取到與當前請求相關的 HttpContext 對象。 |
提示:每個請求都會創建一系列新的上下文對象,當我們獲取到 HttpRequest 或者 HttpResponse 對象的時候,我們都會獲取到一個與當前請求相關聯的全局應用類實例。或者,換句話說,我們不需要擔心定位某個指定請求的上下文對象。
註意到,在上表中我並沒有將應用組件包括在內。你可以通過靜態 HttpContext.Current 屬性來獲取當前請求的 HttpContext 對象,但是,我建議不要使用,因為這在模型與 controller 之前模糊了關註點分離這個原則。如果一個模型需要一個請求的相關信息,那麼我們可以通過從 controller 中的上下文對象中獲取這些信息,將其作為一個方法的參數傳遞給模型就行。這就確保了模型不會介入 controllers 的業務之中,這也使得模型可以進行單元測試,而不需要有任何 ASP.NET 或者 MVC 框架的引用。
Context, Base, 和 Wrapper 類
上表中列出的屬性並不會都返回相同的類型。在非 MVC 框架的組件(全局應用類,處理器,和模塊)中的屬性返回的是一個 HttpContext 對象,基本上就是我們預期想要的。
這些在 MVC 框架誕生之前的上下文對象使得代碼很難進行單元測試,因為它們的耦合度很高,需要我們每次在進行測試之前創建一整套上下文對象,比較繁瑣。
在 MVC 框架中定義的組件的屬性——controller 和 view,可以用來獲取上下文對象並且返回繼承於上下文類的不同類的實例,這提供了很容易的單元測試。Controller 類中的 HttpContext 屬性返回了一個 HttpContextBase 類的實例,所有的上下文對象都是由一個以 Base 為尾碼的類表示的(HttpRequestContext, HttpResponseContext, 等等),它們更容易實例化,配置和進行單元測試。
我們有時候需要根據 ASP.NET 對象創建一個 Base 對象。比如,我們有一個對象,但是需要調用一個以 HttpResponseBase 對象為參數的方法。ASP.NET 類庫中包括了以 Wrapper 為尾碼的類:HttpContextWrapper,HttpRequestWrapper,等等。這些類繼承自 Base 類,在 MVC 中以一個 MVC 友好的 Base 類方式表示 ASP.NET 上下文類(所以 HttpContextWrapper 是繼承自 HttpContextBase,它接收一個 HttpContext 實例作為構造器參數)。Wrapper 類構造器以上下文對象作為構造器參數,並將 HttpContextWrapper 的屬性,方法傳遞給它當中包含的 HttpContext 實例。
我們不能對一個 Base 對象進行拆包——比如將 HttpRequestBase 轉換成 HttpRequest。但是我們總能夠通過靜態的 HttpContext.Current 獲取到我們需要的的上下文對象,它會返回一個 HttpContext 對象。在 controller 中我們需要為這個屬性使用完整的名稱(System.Web.HttpContext.Current),因為在當中定義了一個 HttpContext 屬性,但是返回的是 HttpContextBase 對象。其實,我將 ASP.NET 框架的上下文類與 MVC Base 類視為等同的。
[根據 Adam Freeman – Pro ASP.NET MVC 5 Platform 選譯]