当IIS收到一个http请求,把请求信息发给对应的HttpModel(实际是实现类UrlRoutingModule),在HttpModel中会注册HttpApplication 类中的PostResolveRequestCache事件,通过此事件来动态选择映射HttpHandler处理程序。通过匹配到的RouteData类的RouteHandler属性得到IRouteHandler对象(MVC4是MvcRouteHandler、asp.net原生的是PageRouteHandler),通过这个对象的GetHttpHandler方法就可以得到HttpHandler处理程序。具体代码如下:

  1. public virtual void PostResolveRequestCache(object sender, EventArgs e) {
  2. HttpApplication app = (HttpApplication)sender;
  3. HttpContextBase context = new HttpContextWrapper(app.Context);
  4. //详细见附1
  5. RouteData routeData = RouteCollection.GetRouteData(context);
  6. if (routeData == null) {
  7. return;
  8. }
  9. //详细见附1
  10. IRouteHandler routeHandler = routeData.RouteHandler;
  11. if (routeHandler is StopRoutingHandler) {
  12. return;
  13. }
  14. RequestContext requestContext = new RequestContext(context, routeData);
  15. context.Request.RequestContext = requestContext;
  16. IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
  17. if (httpHandler is UrlAuthFailureHandler) {
  18. if (FormsAuthenticationModule.FormsAuthRequired) {
  19. UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
  20. return;
  21. }
  22. else {
  23. throw new HttpException(401, SR.GetString(SR.Assess_Denied_Description3));
  24. }
  25. }
  26. context.RemapHandler(httpHandler);//动态指定HttpHandler
  27. }

附1:

RouteCollection.GetHttpHandler方法获取匹配当前路由信息的RouteData,此方法是循环遍历RouteCollection集合并调用集合中RouteBase(实现类为Route)的GetRouteData方法,并返回第一个不为Null的Roudata。Roudata实在RoudataBase的GetRouteData方法中被创建的,详见如下代码:

  1. public override RouteData GetRouteData(HttpContextBase httpContext) {
  2. string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
  3. RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults);
  4. if (values == null) {
  5. return null;
  6. }
  7. //为Roudata指定IRouteHandler对象
  8. RouteData routeData = new RouteData(this, RouteHandler);
  9. if (!ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) {
  10. return null;
  11. }
  12. foreach (var value in values) {
  13. routeData.Values.Add(value.Key, value.Value);
  14. }
  15. if (DataTokens != null) {
  16. foreach (var prop in DataTokens) {
  17. routeData.DataTokens[prop.Key] = prop.Value;
  18. }
  19. }
  20. return routeData;
  21. }
  22.  
  23. public class RouteData {
  24. private IRouteHandler _routeHandler;
  25. public RouteData() {
  26. }
  27.  
  28. public RouteData(RouteBase route, IRouteHandler routeHandler) {
  29. Route = route;
  30. RouteHandler = routeHandler;
  31. }
  32. //更多代码
  33. }

 MVC4:动态指定到HttpHandler后,就是处理Controller的创建和Action的执行了

HttpHandler根据Routedata获取到Controller的名称,然后通过ControllerBuilder的静态方法GetControllerFactory获取IControllerFactory的实现类(默认是DefaultControllerFactory )来创建请求的IController的实现类的实例。最后执行IController的Execute方法并传入请求上下文,方法Execute实现在System.Web.Mvc.ControllerBase这个抽象类中,Execute最终调用的是System.Web.Mvc.Controller中的ExecuteCore方法,ExecuteCore方法主要是保存TempData然后根据路由数据调用执行Action。代码如下:

  1. protected override void ExecuteCore()
  2. {
  3. PossiblyLoadTempData();
  4. try
  5. {
  6. string actionName = GetActionName(RouteData);
  7. //ActionInvoker是System.Web.Mvc.Controller的属性,该属性是一个返回类型为IActionInvoker的action执行器
  8. if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
  9. {
  10. HandleUnknownAction(actionName);
  11. }
  12. }
  13. finally
  14. {
  15. PossiblySaveTempData();
  16. }
  17. }

MVC4:执行Action的过程

在HttpHandler调用Controller.Execute方法后,最终将会通过IActionInvoke.ActionInvoker(ControllerContext controllerContext, string actionName)方法来执行Action。System.Web.Mvc.Controller类中有一个ActionInvoker属性,此属性提供IActionInvoke的实现类的实例,用来执行此Controller类中的Action,属性具体的赋值方式如下:

  1. protected virtual IActionInvoker CreateActionInvoker()
  2. {
  3. // Controller supports asynchronous operations by default.
  4. // Those factories can be customized in order to create an action invoker for each request.
  5. IAsyncActionInvokerFactory asyncActionInvokerFactory = Resolver.GetService<IAsyncActionInvokerFactory>();
  6. if (asyncActionInvokerFactory != null)
  7. {
  8. return asyncActionInvokerFactory.CreateInstance();
  9. }
  10. IActionInvokerFactory actionInvokerFactory = Resolver.GetService<IActionInvokerFactory>();
  11. if (actionInvokerFactory != null)
  12. {
  13. return actionInvokerFactory.CreateInstance();
  14. }
  15. // Note that getting a service from the current cache will return the same instance for every request.
  16. return Resolver.GetService<IAsyncActionInvoker>() ??
  17. Resolver.GetService<IActionInvoker>() ??
  18. new AsyncControllerActionInvoker();
  19. }

ActionInvoker与ControllerDescriptor/ActionDescriptor的关系图如下:

由此可以看出Controller选择IActionInvoker的机制是这样的:

1、通过当前的DependencyResolver以IAsyncActionInvokerFactory接口去获取注册的ActionInvoker工厂类,如果返回对象不为Null,则将其CreateInstance方法的返回值作为默认的ActionInvoker。

2、通过当前的DependencyResolver以IActionInvokerFactory接口去获取注册的ActionInvoker工厂类,如果返回对象不为Null,则将其CreateInstance方法的返回值作为默认的ActionInvoker。

3、通过当前的DependencyResolver以IAsyncActionInvoker接口去获取注册的ActionInvoker,如果返回对象不为Null,则将其作为默认的ActionInvoker。

4、通过当前的DependencyResolver以IActionInvoker接口去获取注册的ActionInvoker,如果返回对象不为Null,则将其作为默认的ActionInvoker。

5、创建AsyncControllerActionInvoker对象作为默认的ActionInvoker。

获取到ActionInvoker后,就会执行InvokeAction方法(异步的是BeginInvokeAction/EndInvokeAction),方法中会根据controllerContext先获取ControllerDescriptor、然后根据controllerContext和ActionName通过ControllerDescriptor.FindAction方法获取ActionDescriptor(获取过程中需要通过Action选择器来过滤匹配Action,选择器包含ActionNameSelectorAttribute、ActionMethodSelectorAttribute,具体下面会有描述),获取到ActionDescriptor之后,开始执行Action的筛选器(IActionFilter、IAuthorizationFilter、IExceptionFilter、IResultFilter),在通过ControllerDescriptor获取到ActionDescriptor之后,ActionInvoker会获取到所有的筛选器特性(FilterAttribute),按照四种类别存储在FilterInfo类的四个属性中(ActionFilters、AuthorizationFilters、ExceptionFilters、ResultFilters),然后开始循环验证AuthorizationFilters,(如果其中一个验证没有通过,就立即退出Action后续的执行,根据Result响应回复客户端),如果验证通过后,开始验证IActionFilter(这里看的比较饶人,因为要依次构建一个环环相扣的执行树,而这个树是从执行方向的尾部先构建的,所以为了保证执行顺序不反转就要先反转Filter,然后通过Aggregate方法循环Filte列表,再为每一个Filter建立lamda表达式层层组建为一个嵌套的Fun委托,最后再执行这个组建完成的委托,当其中某一个Filter验证失败的时候,就不再继续执行下一层的Fliter,直接返回Filter的验证失败Result),IActionFilter有两个方法,一个是开始执行Action前执行的方法OnActionExecuting,一个是执行完Action后执行的方法OnActionExecuted,验证不通过将不会执行OnActionExecuted方法。验证过所有的IActionFilter之后(Action也执行了并返回了Result)就开始验证IResultFilter,IResultFilter也要分OnResultExecuting和OnResultExecuted方法大致和IActionFilter差不多。

执行三种筛选器和Action的时候有任何异常,都会被try/catch捕捉到,并开始执行IExceptionFilter筛选器,此筛选器和IAuthorizationFilter差不多。

(需要研究的是ActionResult的执行过程)

ControllerDescriptor与ActionDescriptor之间的关系

ControllerDescriptor、ControllerDescriptor详解

 首先是抽象类型的ControllerDescriptor:

  1. public abstract class ControllerDescriptor : ICustomAttributeProvider
  2. {
  3. public virtual object[] GetCustomAttributes(bool inherit);
  4. public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
  5. public virtual bool IsDefined(Type attributeType, bool inherit);
  6. public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);
  7. //获取指定Action,如果Action上应用了ActionNameSelectorAttribute特性(实现类
  8. //ActionNameAttribute),则会根据特性的IsValidName方法的返回值来匹配
  9. public abstract ActionDescriptor FindAction(ControllerContext controllerContext, string actionName);
  10. //返回此Controller中所有的Action描述类
  11. //(仅限于公有实例方法,但是从.Controller中继承下来的方法除外)
  12. public abstract ActionDescriptor[] GetCanonicalActions();
  13.  
  14. public virtual string ControllerName { get; }
  15. public abstract Type ControllerType { get; }
  16. public virtual string UniqueId { get; }
  17. }
  18. //此类用来获取Controller上的所有自定义特性或指定类型的特性
  19. public interface ICustomAttributeProvider
  20. {
  21. object[] GetCustomAttributes(bool inherit);
  22. object[] GetCustomAttributes(Type attributeType, bool inherit);
  23. //判断指定类型是否应用在Controller上
  24. bool IsDefined(Type attributeType, bool inherit);
  25. }

ControllerDescriptor的默认实现类有两个,这两个都是通过反射的方式解析用于描述Controller的元数据:ReflectedControllerDescriptor、ReflectedAsyncControllerDescriptor,这两个类的区别就在FindAction和GetCanonicalActions两个方法,主要区别在于ReflectedControllerDescriptor是通过反射匹配ActionName而ReflectedAsyncControllerDescriptor要处理ActionName后面的{ActionName}Async和{ActionName}Complete的后缀。

抽象类型ActionDescriptor(Execute方法要深入研究一下):

  1. public abstract class ActionDescriptor : ICustomAttributeProvider
  2. {
  3. //这三个方法同ControllerDescriptor
  4. public virtual object[] GetCustomAttributes(bool inherit);
  5. public virtual object[] GetCustomAttributes(Type attributeType, bool inherit);
  6. public virtual bool IsDefined(Type attributeType, bool inherit);
  7.  
  8. public virtual IEnumerable<FilterAttribute> GetFilterAttributes( bool useCache);
  9. //Action需要接受的所有参数的描述
  10. public abstract ParameterDescriptor[] GetParameters();
  11. //Action方法的执行
  12. public abstract object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);

//ActionSelector用于选择Action,可以自定义选择器(实现ActionMethodSelectorAttribute类)然后

//以特性的方式应用在Action上,如Post Get请求分开的Action,就是通过选择器来实现的。值得注意的是,

//Action的别名选择器特性ActionNameSelectorAttribute的匹配优先于

//ActionMethodSelectorAttribute选择器,虽然优先,但是结果还是会被

//ActionMethodSelectorAttribute选择器过滤

  1. public virtual ICollection<ActionSelector> GetSelectors();
  2. //Action筛选器,总共有四种筛选器IActionFilter、IAuthorizationFilter、IExceptionFilter
  3. //IResultFilter(需要继续深入研究)
  4. public virtual FilterInfo GetFilters();
  5.  
  6. public abstract string ActionName { get; }
  7. public abstract ControllerDescriptor ControllerDescriptor { get; }
  8. public virtual string UniqueId { get; }
  9. }

 抽象类ActionDescriptor的实现类:ReflectedActionDescriptor普通Action、ReflectedAsyncActionDescriptor是XxxAsync/XxxCompleted异步Action、TaskAsyncActionDescriptor是返回类型为Task的Action

asp.net mvc4 Controller与Action执行过程的研究(学习笔记)的更多相关文章

  1. 精通ASP.Net MVC 3 框架(第三版)学习笔记

    精通ASP.Net MVC 3 框架(第三版)学习笔记 代码才是王道. http://pan.baidu.com/s/1pJyL1cn

  2. Javascript的执行过程详细研究

    下面我们以更形象的示例来说明JavaScript代码在页面中的执行顺序.如果说,JavaScript引擎的工作机制比较深奥是因为它属于底层行为,那么JavaScript代码执行顺序就比较形象了,因为我 ...

  3. asp.net mvc4 controller

    controller返回几种返回结果

  4. 控制执行流程——(Java学习笔记三)

    if-else     控制程序流程最基本的形式 格式: if(boolean - expresion){ statement } 或 if(boolean - expresion){ stateme ...

  5. .NET 云原生架构师训练营(对象过程建模)--学习笔记

    目录 UML OPM OPM优化 UML 1997年发布UML标准 主要域 视图 图 主要概念 结构 静态视图 类图 类.关联.泛化.依赖关系.实现.接口 用例视图 用例图 用例.参与者.关联.扩展. ...

  6. Digital Tutors - Creating an Action Adventure Puzzle in Unity学习笔记

    遇到的问题: 1 第11节Scripting the pressure plates中需要获取子物体的Animator组件,教程使用的语句如下: ”SwitchAnim = GetComponentI ...

  7. ASP.NET Core MVC中的IActionFilter.OnActionExecuting方法,可以获取Controller的Action方法参数值

    用过ASP.NET Core MVC中IActionFilter拦截器的开发人员,都知道这是一个非常强大的MVC拦截器.最近才发现IActionFilter的OnActionExecuting方法,甚 ...

  8. asp.net MVC4在Action间跳转 RedirectToAction 传值参数问题

    return RedirectToAction("Test", new { cw = cw, firstdirectoryid = firstdirectoryid }); 上式中 ...

  9. (转载)js引擎的执行过程(一)

    概述 js是一种非常灵活的语言,理解js引擎的执行过程对我们学习javascript非常重要,但是网上讲解js引擎的文章也大多是浅尝辄止或者只局部分析,例如只分析事件循环(Event Loop)或者变 ...

随机推荐

  1. css 行内元素和块级元素

    1. 块级元素默认在新行开始,如常见的div和p标签,行内元素默认在同行开始显示,如a,span标签 2.块级元素一般用于做容器,可容纳行内和块级元素,可设置width和height,行内元素只能容纳 ...

  2. careercup-数学与概率 7.6

    7.6 在二维平面上,有一些点,请找出经过点数最多的那条线. 解法: 类似于leetcode:Max Points on a Line 我们只需在任意两点之间“画”一条无限长的直线(也即不是线段),并 ...

  3. oracle表锁住 解锁办法

    第一种方法: 用系统账户如sys      as  SYSDBA 登录进去 1.查看数据库锁,诊断锁的来源及类型:  select object_id,session_id,locked_mode f ...

  4. 2016iweb峰会参会总结

    2016年8月27日去国家会议中心参加iweb峰会. 8点半开始签到入场,8点20分排队签到的人已经排到另一个门口,人超级多啊. 9点一如既往的由h5女神娜姐开场. 上午场 基本是各公司的大佬们介绍各 ...

  5. WebService 实现BS环境与BS环境传递参数,根据参数生成txt文档

    客户端: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Client.as ...

  6. 修改浏览器User-Agent

    IE: 1,F12进入开发人员工具 2,工具->更改用户代理字符串->自定义 3,在"友好名称"中填入"IE9",在"用户代理字符串&qu ...

  7. JVM工作原理和特点

    操作系统装入JVM,是通过jdk中的java.exe来完成,通过以下4个步骤: 1. 创建JVM装载环境和配置: JVM装入系统,JVM提供的方式是操作系统的动态链接文件.文件就是一个装入路径的问题, ...

  8. 企业级应用框架(五)IOC容器在框架中的应用

    前言 在上一篇我大致的介绍了这个系列所涉及到的知识点,在本篇我打算把IOC这一块单独提取出来讲,因为IOC容器在解除框架层与层之间的耦合有着不可磨灭的作用.当然在本系列前面的三篇中我也提供了一种基于反 ...

  9. C#的类型、变量和值

    大学学了C#,工作也是使用C#,虽然在日常的开发中没什么大的问题,但个人觉得在C#的理解还不是很清晰,所以决定花一定的时间来理一理学过的知识,顺便革新下脑袋里的知识,因为坑爹的学校在教.net的时候, ...

  10. LSJ_NHibernate第二章 ManagerPage

    前言: 项目为传统的三层架构,可以根据个人的需求进行拓展. 很多人都在质疑B层的作用,我认为B层才是核心,这个取决于业务的复杂度 项目的结构也比较的简单,我们先从最底层说起,ManagerPage,这 ...