ASP.NET MVC 框架中的 MvcRouteHandler 和 MvcHandler






4.90/5 (10投票s)
本文详细介绍了 MvcRouteHandler 和 MvcHandler 类的作用。
引言
当 MVC 收到请求时,路由引擎负责将请求 URL 与已注册的路由进行匹配。找到匹配的路由后,将调用该路由的路由处理器。每个路由都可以有自己的路由处理器。路由处理器是一个实现 IRouteHandler
接口的类。
IRouteHandler 接口
它定义了一个契约,要求类必须实现该契约才能处理匹配的路由模式的请求。它公开了一个名为 GetHttpHandler()
的方法,该方法负责提供将要处理该请求的路由处理器类实例。该接口的定义如下:
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// See License.txt in the project root for license information.
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
通过 MapRoute
扩展方法添加的任何路由都由默认路由处理器处理,即 System.Web.Mvc
命名空间中定义的 MvcRouteHandler
类。
MvcRouteHandler 类
路由处理器负责通过检查收到的 RequestContext
来确定将要处理请求的 HTTP 处理器。MvcRouteHandler
类的实现如下所示:
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// See License.txt in the project root for license information.
namespace System.Web.Mvc
{
using System.Web.Routing;
using System.Web.SessionState;
public class MvcRouteHandler : IRouteHandler {
private IControllerFactory _controllerFactory;
public MvcRouteHandler() {
}
public MvcRouteHandler(IControllerFactory controllerFactory) {
_controllerFactory = controllerFactory;
}
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
requestContext.HttpContext.SetSessionStateBehavior(
GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
protected virtual SessionStateBehavior
GetSessionStateBehavior(RequestContext requestContext) {
string controllerName =
(string)requestContext.RouteData.Values["controller"];
IControllerFactory controllerFactory = _controllerFactory ??
ControllerBuilder.Current.GetControllerFactory();
return controllerFactory.GetControllerSessionBehavior
(requestContext, controllerName);
}
#region IRouteHandler Members
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
return GetHttpHandler(requestContext);
}
#endregion
}
}
从上面的代码可以清楚地看出,该类实现了 IRouteHandler
接口的 GetHttpHandler()
方法,该方法执行两项操作:首先,它调用 SetSessionStateBehavior()
方法为当前的 Http 请求设置处理请求所需的会话状态支持,通过传递 SessionStateBehavior
枚举值来指定适用于该请求的会话状态行为类型。为了获取会话状态行为,会调用 GetSessionStateBehavior()
方法,该方法获取控制器名称并通过 ControllerBuilder
类查找控制器工厂。ControllerBuilder
类返回 DefaultControllerFactory
类的引用(该类是 IControllerFactory
接口的一种类型,假定我们没有自定义控制器工厂)。调用 DefaultControllerFactory
类的 GetControllerSessionBehavior()
方法,并传递当前的 RequestContext
和控制器类型,该方法返回一个 SessionStateBehavior
枚举值。现在,在设置了 SessionStateBehavior
之后,它将继续创建 MvcHandler
类的实例,该类是默认的 ASP.NET MVC HTTP 处理器,用于处理请求。
MvcHandler 类
MvcHandler
类负责为正在处理的请求生成响应。MvcHandler
类从 MvcRouteHandler
类在 GetHttpHandler()
方法实现中传递给其构造函数的 RequestContext
接收有关正在进行的请求的信息。MvcHandler
类实现了三个接口:IHttpAsyncHandler
、IHttpHandler
和 IRequiresSessionState
。
IRequiresSessionState
接口,当实现该接口时,表示当前的 HTTP 处理器需要对会话状态值进行读写访问。这是一个标记接口,没有任何方法需要实现。
IHttpHandler
接口定义了一个契约,要求类必须实现该契约才能使用 HTTP 处理器同步处理 HTTP Web 请求。它公开了一个名为 ProcessRequest()
的方法。IHttpAsyncHandler
接口是 IHttpHandler
接口的异步版本。
MvcHandler
类实现 IHttpHandler
的方式如下:
// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved. See License.txt in the project root for license information.
void IHttpHandler.ProcessRequest(HttpContext httpContext)
{
ProcessRequest(httpContext);
}
protected virtual void ProcessRequest(HttpContext httpContext)
{
HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
ProcessRequest(iHttpContext);
}
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
SecurityUtil.ProcessInApplicationTrust(() => {
IController controller;
IControllerFactory factory;
ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
});
}
ProcessRequest()
方法将 HttpContext
转换为更通用的容器——HttpContextWrapper
类。HttpContextWrapper
类是 HttpContext
的包装器,并扩展了 HttpContextBase
类。现在的问题是我们为什么使用 HttpContextWrapper
而不是 HttpContext
。原因是 HttpContext
类没有基类,也不是虚拟的,因此无法在单元测试中使用它。因此,我们使用 HttpContextBase
类,该类是 abstract
的,并包含与 HttpContext
类相同的成员。HttpContextBase
类允许创建与 HttpContext
类相似的派生类,可用于单元测试。
获取 HttpContextBase
后,会调用其内部版本的 ProcessRequest()
方法,并将 HttpContextBase
参数传递给它。然后,该调用会进一步转发到 ProcessRequestInit()
,该方法负责从 RouteData
集合中提取控制器名称,并使用请求的控制器工厂来创建相应的控制器。
// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved. See License.txt in the project root for license information.
internal static readonly string MvcVersion = GetMvcVersionString();
public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
internal ControllerBuilder ControllerBuilder {
get {
if (_controllerBuilder == null) {
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set {
_controllerBuilder = value;
}
}
private void ProcessRequestInit(HttpContextBase httpContext,
out IController controller, out IControllerFactory factory) {
// If request validation has already been enabled, make it lazy.
// This allows attributes like [HttpPost] (which looks
// at Request.Form) to work correctly without triggering full validation.
bool? isRequestValidationEnabled =
ValidationUtility.IsValidationEnabled(HttpContext.Current);
if (isRequestValidationEnabled == true) {
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
}
AddVersionHeader(httpContext);
RemoveOptionalRoutingParameters();
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);
if (controller == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
}
protected internal virtual void AddVersionHeader(HttpContextBase httpContext) {
if (!DisableMvcResponseHeader) {
httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
}
}
private static string GetMvcVersionString() {
// DevDiv 216459:
// This code originally used Assembly.GetName(),
// but that requires FileIOPermission, which isn't granted in
// medium trust. However, Assembly.FullName *is* accessible in medium trust.
return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
}
ProcessRequestInit()
接收包含 Http 特定信息以及 IController
和 IControllerFactory
类型引用的 HttpContextBase
对象,最终将保存创建的控制器和控制器工厂对象的副本。它首先添加响应的头信息。
然后,为了获取负责创建任何控制器类实例的控制器工厂,会使用 ControllerBuilder
类。在 ProcessRequestInit()
方法中,ControllerBuilder
属性的引用在没有自定义控制器工厂的情况下返回 DefaultControllerFactory
类的实例。获取控制器工厂实例后,便通过调用 DefaultControllerFactory
类的 CreateController()
方法来实例化控制器。您可以在 此处 找到有关 DefaultControllerFactory
类的更多信息。
获取控制器实例后,将为此控制器调用 Execute()
方法。execute
方法负责调用名称与路由数据中的 action
值匹配的 action
方法。要了解有关 Execute()
方法工作原理的更多信息,请参阅我 之前的帖子。
在 Execute()
方法完成后,会调用 DefaultControllerFactory
类中的 ReleaseController()
方法,以释放控制器实例。
ControllerBuilder 类
ControllerBuilder
是一个单例类,负责生成 IControllerFactory
类型的实例。ControllerBuillder
类有两个构造函数:一个默认构造函数,另一个是带参数的构造函数。
// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved. See License.txt in the project root for license information.
private Func<IControllerFactory> _factoryThunk = () => null;
private static ControllerBuilder _instance = new ControllerBuilder();
private IResolver<IControllerFactory> _serviceResolver;
public ControllerBuilder()
: this(null) {
}
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) {
_serviceResolver = serviceResolver ??
new SingleServiceResolver<IControllerFactory>(
() => _factoryThunk(),
new DefaultControllerFactory { ControllerBuilder = this },
"ControllerBuilder.GetControllerFactory"
);
}
public static ControllerBuilder Current {
get {
return _instance;
}
}
[SuppressMessage("Microsoft.Design",
"CA1024:UsePropertiesWhereAppropriate", Justification =
"Calling method multiple times might return different objects.")]
public IControllerFactory GetControllerFactory() {
return _serviceResolver.Current;
}
public HashSet<string> DefaultNamespaces {
get {
return _namespaces;
}
}
默认构造函数调用带参数的构造函数,并传递 null
作为参数,表明 SingleServiceResolver
对象将用作默认服务解析器,该解析器保存在 _serviceResolver
中。现在,每当我们通过 Current
属性需要 ControllerBuilder
对象引用时,我们都可以获取到它。为了在类外部获取创建的 DefaultControllerFactory
类实例,可以使用 GetControllerFactory()
方法。
ControllerBuilder
类还公开了 DefaultNamespaces
属性,用于在应用程序开始时定义默认命名空间。每当通过此属性提供默认命名空间时,DefaultControllerFactory
都会使用这些命名空间来查找控制器类型。
除了上述实现之外,ControllerBuilder
类还公开了 SetControllerFactory()
方法,该方法充当 setter 方法,允许您将默认控制器工厂更改为自定义控制器工厂。