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
其实就做了两个事:
- 设置SetSessionStateBehavior的行文
- 返回了一个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
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
_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。
- 里面返回了_serviceResolver的SingleServiceResolver
来看一下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的实例。返回。这两个方法会在下一篇说到。