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

MVC 源码系列之控制器激活(一)的更多相关文章

  1. MVC 源码系列之控制器激活(二)之GetControllerType和GetcontrollerInstance

    GetControllerType和GetcontrollerInstance GetControllerType protected internal virtual Type GetControl ...

  2. MVC 源码系列之控制器执行(一)

    控制器的执行 之前说了Controller的激活,现在看一个激活Controller之后控制器内部的主要实现. public interface IController { void Execute( ...

  3. MVC 源码系列之控制器执行(二)

    ## 控制器的执行 上一节说道Controller中的ActionInvoker.InvokeAction public virtual bool InvokeAction(ControllerCon ...

  4. MVC 源码系列之路由(一)

    路由系统 注释:这部分的源码是通过Refector查看UrlRoutingModule的源码编写,这部分的代码没有写到MVC中,却是MVC的入口. 简单的说一下激活路由之前的一些操作.一开始是由MVC ...

  5. MVC 源码系列之路由(二)

    MVCParseData和Match方法的实现 ### ParseData 那么首先要了解一下ParseData. //namespace Route public string Url { get ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  9. [转]MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    本文转自:http://www.cnblogs.com/landeanfen/p/5989092.html 阅读目录 一.MVC原理解析 1.MVC原理 二.HttpHandler 1.HttpHan ...

随机推荐

  1. uni-app中页面部分内容使用索引列表(uni-indexed-list),动态数据

    一.引入uni-indexed-list.uni-icons组件 从uni-app插件市场下载或从HBuilder X提供的hello uni-app模板中复制 二.页面中引用 三.对请求获得的数据处 ...

  2. 卷积神经网络基础(CNN)【转载】

    作者: Sanjay Chan [ http://blog.csdn.net/chenzomi ] 背景 之前在网上搜索了好多好多关于CNN的文章,由于网络上的文章很多断章取义或者描述不清晰,看了很多 ...

  3. 关于prepareStatement(String sql,int autoGeneratedKeys)的记录

    PreparedStatement prepareStatement(String sql,int autoGeneratedKeys) throws SQLException autoGenerat ...

  4. Windows 实用软件

    Useful tool Listary Ditto Winsnap Quick Look Myper Splash GifCam ScreenToGif Free Download Manage Si ...

  5. TreeView详细用法

    Treeview用于显示按照树形结构进行组织的数据.          Treeview控件中一个树形图由节点(TreeNode)和连接线组成.TtreeNode是TTreeview的基本组成单元. ...

  6. css实现div内凹角样式

    平常的开发中我们一般使用到圆角都是外凸的,即border-radius属性.而如果有内凹角的情况,我们一般的考虑实现方法有2种.一种是直接使用背景图片,一种是使用css. 用到的属性则是backgro ...

  7. 10年前文章_UC3A/B 开发环境设置

    大部分设置和 Z32U 交叉编译环境的配置 类似 Windows 环境 步骤二: 安装 toolchain 和mkII lite V2 的驱动 安装运行 avr32-gnu-toolchain-2.0 ...

  8. socket客户端的备份机制

    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); //设定服务器的地址信息 SOCKADDR_IN addrSrv; addrSrv.sin_a ...

  9. [HTTP知识体系]前端常用的一些参数

    1.http常见状态码(status code) 200(成功) 服务器已成功处理了请求.通常,这表示服务器提供了请求的网页. 301 (永久移动) 请求的网页已永久移动到新位置. 服务器返回此响应( ...

  10. PHP 图片合成、仿微信群头像

    PHP 图片合成.仿微信群头像 参考文章: 作者:凯歌~,php图片合成方法(多张图片合成一张). 经过测试,略作调整和注释,感谢分享. 欢迎提出改善优化意见! 示例代码: /** * 合成图片 * ...