• 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的实例。返回。这两个方法会在下一篇说到。

  • 相关阅读:
    Jmeter beanshell preprocessor随机添加任意多个请求参数
    Jmeter 场景设计
    jmeter 参数化
    .net 匿名方法
    jmeter 运行脚本报错 java.net.BindException: Address already in use
    Jmeter mysql性能测试
    ngcordova 监控网络制式改变
    建立apk定时自动打包系统第一篇——Ant多渠道打包并指定打包目录和打包日期
    Kafka架构
    Linux命令
  • 原文地址:https://www.cnblogs.com/shaoqi/p/7368236.html
Copyright © 2020-2023  润新知