65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 框架中的 MvcRouteHandler 和 MvcHandler

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (10投票s)

2013年5月20日

CPOL

5分钟阅读

viewsIcon

93393

本文详细介绍了 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 类实现了三个接口:IHttpAsyncHandlerIHttpHandlerIRequiresSessionState

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 特定信息以及 IControllerIControllerFactory 类型引用的 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 方法,允许您将默认控制器工厂更改为自定义控制器工厂。

© . All rights reserved.