使用 HttpApplication 對象 ASP.NET 框架中的許多類都提供了許多很方便的屬性可以直接映射到 HttpContext 類中定義的屬性。這種交疊有一個很好的例子就是 HttpApplication,它就是全局應用類的基類,在下表中,你可以看到 HttpApplication 類中定
使用 HttpApplication 對象
ASP.NET 框架中的許多類都提供了許多很方便的屬性可以直接映射到 HttpContext 類中定義的屬性。這種交疊有一個很好的例子就是 HttpApplication,它就是全局應用類的基類,在下表中,你可以看到 HttpApplication 類中定義的屬性和方法。許多和 HttpContext 中定義的很相似。
表 1 – HttpApplication 類中定義的成員
名稱 | 描述 |
Application | 對應到 HttpContext.Application 屬性,通過它可以獲取應用層面的狀態數據。 |
CompleteRequest() | 終止當前請求的生命周期,直接跳轉到 LogRequest 事件。 |
Context | 返回當前請求的 HttpContext 對象。 |
Init() | 每個註冊模塊上的 Init() 方法調用之後調用。 |
Modules | 返回一個 HttpModuleCollection 對象,當中詳細描述了當前應用中的模塊。 |
RegisterModule(type) | 註冊新模塊的靜態方法。 |
Request | 返回 HttpContext.Request 值, 但是如果值為 null 的時候會拋出一個 HttpException。 |
Response | 返回 HttpContext.Response 值,但是如果值為 null 的時候會拋出一個 HttpException。 |
Server | 映射到 HttpContext.Server。 |
Session | 返回 HttpContext.Session 值,但是如果值為 null 的時候會拋出一個 HttpException。 |
大多數的這些成員都是很方便的屬性可以映射到 HttpContext 類中的屬性,但是有三個值得註意,接下來詳細說明。
處理屬性異常
Request, Response, Session 和 User 屬性返回的是 HttpContext 類中相對應的屬性的值,但是,這些屬性如果從 HttpContext 上對應的屬性中獲取的值是 null 就會拋出一個 HttpException。
發生這樣的事是因為 HttpApplication 類會為兩種不同的生命周期接收通知:應用生命周期和請求生命周期。描述單個請求的對象是不可以在全局應用類中用來處理應用相關的事件,所以如果我們在處理應用層級的通知的時候使用了與請求相關聯的屬性就會拋出 HttpException 異常。
拋出這樣一個異常的策略是非常粗糙的,因為這使得處理未知來源的 HttpApplication 對象非常困難,我們可以看一下下麵的代碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using System.Web.Routing; 7 8 namespace SimpleApp 9 { 10 public class MvcApplication : System.Web.HttpApplication 11 { 12 public MvcApplication() 13 { 14 PostAcquireRequestState += (src, args) => CreateTimeStamp(); 15 } 16 17 protected void Application_Start() 18 { 19 AreaRegistration.RegisterAllAreas(); 20 RouteConfig.RegisterRoutes(RouteTable.Routes); 21 CreateTimeStamp(); 22 } 23 24 private void CreateTimeStamp() 25 { 26 string stamp = Context.Timestamp.ToLongTimeString(); 27 if (Session != null) 28 { 29 Session["request_timestamp"] = stamp; 30 } 31 else 32 { 33 Application["app_timestamp"] = stamp; 34 } 35 } 36 } 37 }View Code
現在,我們刪除我們之前寫的代碼,重新定義一個新的方法用來創建時間戳並將其存放在狀態數據中。現在,我們不需要對狀態數據瞭解太多,只需要知道 Session 屬性會為每一個獨立的請求返回狀態數據對象。
提示:註意到,我為 PostAcquireRequestState 事件創建了請求時間戳。這是因為模塊直到 AcquireRequestState 事件觸發之後才會提供狀態數據服務。因此,即使全局應用類實例創建出來用來處理請求相關的事件,Session 屬性也會返回 null。
這個新方法叫做 CreateTimeStamp,旨在減少代碼冗餘併為應用層級和請求層級的事件添加事件戳。但是,只要現在應用一啟動就會報錯,因為在這段代碼中,在全局應用類創建出來處理 Application_Start 方法的時候想要嘗試讀取 Session 屬性。
我能夠使用 try…catch 代碼塊,但是這是一個糟糕的實踐,因為異常處理不應該用來管理一個應用中可預見的流程。因此,我們應該直接使用 HttpContext 中同等的屬性。代碼如下:
1 private void CreateTimeStamp() 2 { 3 string stamp = Context.Timestamp.ToLongTimeString(); 4 if (Context.Session != null) 5 { 6 Session["request_timestamp"] = stamp; 7 } 8 else 9 { 10 Application["app_timestamp"] = stamp; 11 } 12 }View Code
註意到,我只是修改了之前對 Session 屬性的檢查;如果它不為 null,我就可以使用 HttpApplication 類中定義的 Session 屬性。Application 屬性總是能夠返回 HttpApplicationState 對象,狀態管理相關的知識我們會在後期講解到。
我們需要註意這種問題特別是在全局應用類中不與獨立請求相關聯的對象。ASP.NET 其他地方,特別是在 MVC 框架 controller 類中。我們總是可以使用 context 來表示一個請求。我們可以看一下下麵的例子:
1 using SimpleApp.Models; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 8 namespace SimpleApp.Controllers 9 { 10 public class HomeController : Controller 11 { 12 public ActionResult Index() 13 { 14 return View(GetTimeStamps()); 15 } 16 17 [HttpPost] 18 public ActionResult Index(Color color) 19 { 20 Color? oldColor = Session["color"] as Color?; 21 if (oldColor != null) 22 { 23 Votes.ChangeVote(color, (Color)oldColor); 24 } 25 else 26 { 27 Votes.RecordVote(color); 28 } 29 30 ViewBag.SelectedColor = Session["color"] = color; 31 return View(GetTimeStamps()); 32 } 33 34 private List<string> GetTimeStamps() 35 { 36 return new List<string> { String.Format("App timestamp: {0}", HttpContext.Application["app_timestamp"]), 37 String.Format("Request timestamp: {0}", Session["request_timestamp"])}; 38 } 39 } 40 }View Code
Controller 類是 MVC 框架的基礎,當中提供了一個方便的 Session 屬性與 HttpContext.Session 相關聯(但是我需要使用 HttpContext.Application,因為它並沒有相關聯的簡便的屬性)。
你可以在應用啟動的時候看到時間戳。因為兩個值在起初的時候很可能是相同的,但是如果你重新刷新瀏覽器視窗的話,你會發現應用時間戳一直不變,而請求時間戳會在每次更新的時候發生改變。
圖 1 - 展示應用和請求時間戳
[根據 Adam Freeman – Pro ASP.NET MVC 5 Platform 選譯]