MVC 源码系列之控制器激活(一)

Controller的激活

上篇说到Route的使用,GetRoute的方法里面获得RouteData。然后通过一些判断,将最后的RouteData中的RouteHandler添加到context.RemapHandler。这个方法的意思就是将请求重新映射到这个handler中出来。在上一篇也说到了这个Handler其实是MvcRouteHandler。

 RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null)
{
    IRouteHandler routeHandler = routeData.RouteHandler;
    if (routeHandler == null)
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
    }
    if (!(routeHandler is StopRoutingHandler))
    {
        RequestContext requestContext = new RequestContext(context, routeData);
        context.Request.RequestContext = requestContext;
        
        //注意着句  这就是在得到的MvcRouteHandler 调用GetHttpHandler获得HttpHandler
        IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
        
        if (httpHandler == null)
        {
            object[] args = new object[] { routeHandler.GetType() };
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), args));
        }
        if (httpHandler is UrlAuthFailureHandler)
        {
            if (!FormsAuthenticationModule.FormsAuthRequired)
            {
                throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
            }
            UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
        }
        else
        {
            context.RemapHandler(httpHandler);
        }
    }

那看看routeHandler.GetHttpHandler,也就是MvcRouteHandler.GetHttpHandler是如何处理请求的。

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"];
    if (String.IsNullOrWhiteSpace(controllerName))
    {
        throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
    }

    IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
    return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
}

这个是MVCRouteHandler里面的。GetHttpHandler的实现调用了一个HttpContext的SetSessionStateBehavior(设置session状态的行为)。然后返回了一个MVCHandler。所以GethttpHandler
其实就做了两个事:

  1. 设置SetSessionStateBehavior的行文
  2. 返回了一个MVCHandler

返回的MVCHandler就会被下面这行语句调用,然后开始处理请求。但我们还是想看一下GetSessionStateBehavior这个方法。这个流程中会有寻找ControllerFactory的过程,这个类之后也会用到。

context.RemapHandler(httpHandler);

寻找ControllerFactory

 requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));

其实里面的参数是调用了GetSessionStateBehavior的方法之后返回的SessionStateBehavior这属性。主要看SessionStateBehavior这个方法。首先获取了RouteData中的controller的value,如果为空则报错。然后通过下面代码获得ControllerFactory,然后调用ControllerFactory的GetControllerSesionBehavior。

IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();

controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);

先看第一句,先看_controllerFactory和ControllerBuilder。

//MvcRouteHandler
private IControllerFactory _controllerFactory;

public MvcRouteHandler(IControllerFactory controllerFactory)
{
    _controllerFactory = controllerFactory;
}

在MvcRouteHandler中可以看到_controllerFactory是一个属性,而且是在构造MvcRouteHandler的时候进行赋值的。在Route的MapRoute核心实现中

//这是在RouteCollectionExtensions类里面的 MapRoute方法。
Route route = new Route(url, new MvcRouteHandler())
{
    Defaults = CreateRouteValueDictionaryUncached(defaults),
    Constraints = CreateRouteValueDictionaryUncached(constraints),
    DataTokens = new RouteValueDictionary()
};

构造函数里面是没有传任何参数的。所以这个是为null的。那么ControllerBuilder是什么呢?如果看源码的话可以看到这个是

public class ControllerBuilder
{
    private static ControllerBuilder _instance = new ControllerBuilder();
    
    private Func<IControllerFactory> _factoryThunk = () => null;
    
    private IResolver<IControllerFactory> _serviceResolver;
    
    internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
    {
        _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                                                  () => _factoryThunk(),
                                                  new DefaultControllerFactory { ControllerBuilder = this },
                                                  "ControllerBuilder.GetControllerFactory");
    }
    
    public static ControllerBuilder Current
    {
        get { return _instance; }
    }
    
    public IControllerFactory GetControllerFactory()
    {
        return _serviceResolver.Current;
    }
    
    public void SetControllerFactory(IControllerFactory controllerFactory)
    {
        if (controllerFactory == null)
        {
            throw new ArgumentNullException("controllerFactory");
        }

        _factoryThunk = () => controllerFactory;
    }
}

通过 ControllerBuilder.Current.GetControllerFactory() 这个代码的话,可以看到首先调用了静态属性Current,属性Current返回了_instance的字段,是创建了一个ControllerBuilder,没有传值,也就是调用下面的构造函数的时候 serviceResolver是为空的,所以_serviceResolver是new了一个SingleServiceResolver类。返回的Currnet调用了GetControllerfactory(),刚好是前面的_serviceResolver字段的Current。当然 可以通过SetControllerFactory方法来进行 _factoryThunk的设置。

internal class SingleServiceResolver<TService> : IResolver<TService>
    where TService : class
{
    private Lazy<TService> _currentValueFromResolver;
    private Func<TService> _currentValueThunk;
    private TService _defaultValue;
    private Func<IDependencyResolver> _resolverThunk;
    private string _callerMethodName;
    
    //currentvalueThunk= () => _factoryThunk(),  _factoryThunk = () => null;
    //DefaultValue= new DefaultControllerFactory { ControllerBuilder = this },
    //callerMethodName = "ControllerBuilder.GetControllerFactory"
    
    public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)
    {
        if (currentValueThunk == null)
        {
            throw new ArgumentNullException("currentValueThunk");
        }
        if (defaultValue == null)
        {
            throw new ArgumentNullException("defaultValue");
        }

        _resolverThunk = () => DependencyResolver.Current;
        
        _currentValueFromResolver = new Lazy<TService>(GetValueFromResolver);
        _currentValueThunk = currentValueThunk;
        _defaultValue = defaultValue;
        _callerMethodName = callerMethodName;
    }
    
    public TService Current
    {
        get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
    }
    
    private TService GetValueFromResolver()
    {
        TService result = _resolverThunk().GetService<TService>();

        if (result != null && _currentValueThunk() != null)
        {
            throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.SingleServiceResolver_CannotRegisterTwoInstances, typeof(TService).Name.ToString(), _callerMethodName));
        }

        return result;
    }
}

internal interface IResolver<T>
{
    T Current { get; }
}

可以看出IResolver只有一个T的Current的属性。重点还是在SingleServiceResolver,上面代码调用的是这个构造函数。我将参数放到上面便与理解。我们也看到了Current的逻辑链,我们先看后面两个,因为这两是我们参数赋值的。_currentValueThunk()的为 () => _factoryThunk()也就是 _factoryThunk = () => null(默认的情况是这样,如果通过SetControllerFactory设置了 _factoryThunk()的话就会返回设置的ControllerFactory),所以会直接轮到 _defaultValue也就是默认的ControllerFactory的类型。那前面这个 _currentValueFromResolver是什么呢?是一个Lazy(GetValueFromResolver)的方法。这个方法里面会先去调用 _resolverThunk的GetSevice的方法。

_resolverThunk = () => DependencyResolver.Current;

终于看到DependencyResolver这个类了,DependencyResolver这个其实实现第三方DI的类类型。我们可以使用下面这种方法进行注册

//这个是注册DependencyResolver的方法 不是在源码中的  AutofacDependencyResolver实现了 IDependencyResolver 
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

当然默认的情况下是没有类注册的上面的result也是为null的,那么如果什么都没做的话便会使用默认的ControllerFactory。这样优先级也出来了。

  • 通过DependencyResolver.SetResolver的优先级最高
  • 通过SetControllerFactory的第二
  • 如果都没有就会使用默认的ControllerFactory。

说了这么多,理一下整个获取ControllerFactory流程和逻辑:

  • 首先看MVCRouteHandler的构造时_controllerFactory属性有没有赋值
  • 没有话调用ControllerBuilder.Current.GetControllerFactory()
    • 里面返回了_serviceResolver的SingleServiceResolver的Current属性
    • SingleServiceResolver 判断DependencyResolver和_factoryThunk是否为空,为空返回默认的DefaultControllerFactory。

来看一下DefaultControllerFactory的GetControllerSessionBehavior的方法。

protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
    if (controllerType == null)
    {
        return SessionStateBehavior.Default;
    }

    return _sessionStateCache.GetOrAdd(
        controllerType,
        type =>
        {
            var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
                .OfType<SessionStateAttribute>()
                .FirstOrDefault();

            return (attr != null) ? attr.Behavior : SessionStateBehavior.Default;
        });
}

使用了一个_sessionStaeCache的GetorAdd方法,具体的就不看了,主要是了解上面找ControllerFactory的过程。

MVCHandler

思路在回到上面,MvcHandler被放到了下面这句代码,意思就是将请求映射到httphandler,里面细节看不太懂就不深究了,有时间在看。直接看MvcHandler的ProcessRequest方法。

context.RemapHandler(httpHandler);
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
{

    protected virtual void ProcessRequest(HttpContext httpContext)
    {
        HttpContextBase httpContextBase = new HttpContextWrapper(httpContext);
        ProcessRequest(httpContextBase);
    }
    
    protected internal virtual void ProcessRequest(HttpContextBase httpContext)
    {
        IController controller;
        IControllerFactory factory;
        ProcessRequestInit(httpContext, out controller, out factory);

        try
        {
            controller.Execute(RequestContext);
        }
        finally
        {
            factory.ReleaseController(controller);
        }
    }
    
    private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
    {

        //如果请求验证已经启用,请使其懒惰。 这允许像[HttpPost](它查找Request.Form)的属性正常工作,而不会触发完全验证。 容忍null HttpContext进行测试。
        HttpContext currentContext = HttpContext.Current;
        if (currentContext != null)
        {
            bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext);
            if (isRequestValidationEnabled == true)
            {
                ValidationUtility.EnableDynamicValidation(currentContext);
            }
        }

        AddVersionHeader(httpContext);
        RemoveOptionalRoutingParameters();

        string controllerName = RequestContext.RouteData.GetRequiredString("controller");
        
        //又是ControllerBuilder
        factory = ControllerBuilder.GetControllerFactory();
        controller = factory.CreateController(RequestContext, controllerName);
        
        if (controller == null)
        {
            throw new InvalidOperationException(
                String.Format(
                    CultureInfo.CurrentCulture,
                    MvcResources.ControllerBuilder_FactoryReturnedNull,
                    factory.GetType(),
                    controllerName));
        }
    }

}

核心方法ProcessRequest,首先将请求放到上下文包装类中,将httpContextBase传入ProcessRequest方法内,调用了ProcessRequestInit的方法,实例化了controller和factory。所以controller方法是最和核心的。

方法内首先获得类型为HttpContext的currentContext,对其进行验证。然后在请求头中添加MVC的信息(AddVersionHeader)。然后去掉RouteData中的为UrlParameter.Optional的参数。然后获得了Controller的名字,然后通过ControllerBuilder获取controllerFactory.不过这个ControllerBuilder,不过里面的实现如果没有去进行赋值的话也是会走上面的流程的。

internal ControllerBuilder ControllerBuilder
{
    get
    {
        if (_controllerBuilder == null)
        {
            _controllerBuilder = ControllerBuilder.Current;
        }
        return _controllerBuilder;
    }
    set { _controllerBuilder = value; }
}

如果什么都没设置走默认流程。

controller = factory.CreateController(RequestContext, controllerName);

看一下DefaultControllerFactory里面的CreateController。

public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
    if (requestContext == null)
    {
        throw new ArgumentNullException("requestContext");
    }

    if (String.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
    {
        throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    }

    Type controllerType = GetControllerType(requestContext, controllerName);
    IController controller = GetControllerInstance(requestContext, controllerType);
    return controller;
}

一开始入口检查,然后通过GetControllerType的方法获得ControllerType的类型,controller的实例。返回。这两个方法会在下一篇说到。

原文地址:https://www.cnblogs.com/shaoqi/p/7368236.html