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 选译]