接着上一篇, 在创建好Controller之后, 有一个 this.ExecuteCore()方法, 这部分是执行的. 那么里面具体做了些什么呢?

//ControllerBase
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
}
this.VerifyExecuteCalledOnce();
   //在这里创建了控制器上下文, ControllerContext
this.Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
     //加载 TempData, 创建及执行 Action, 处理 Action 返回的 ActionResult, 保存TempData
this.ExecuteCore();
}
}

来看一下这里的 ExecuteCore具体是执行的那里的方法.

//System.Web.MVC.Controller
protected override void ExecuteCore()
{
   //从Session 中加载 TempData 数据
this.PossiblyLoadTempData();
try
{
     //从路由中获取 Action 名称
string requiredString = this.RouteData.GetRequiredString("action");
     //判断部分会去执行 Action, 并处理 Action 返回的ActionResult
if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString))
{
this.HandleUnknownAction(requiredString);
}
}
finally
{
     //保存 TempData 数据
this.PossiblySaveTempData();
}
}

这个类应该还是蛮熟悉的吧, 我们创建的控制器类, 都会直接或者间接继承这个类.

一、解析

1. PossiblyLoadTempData()

首先来看一下这个方法吧. 看看里面做了些什么

internal void PossiblyLoadTempData()
{
if (!base.ControllerContext.IsChildAction)
{
base.TempData.Load(base.ControllerContext, this.TempDataProvider);
}
}

看到这里的TempData, 感觉好熟悉吧. 我还是唠叨一句吧.

TempData是保存在session中的, 可以用于不同Controller,不同Action,Action到View之间的传值.

额, 要不要继续解析一下呢, 算了, 看一下吧, 起码看到这个数据是保存在session中的.

//TempDataDictionary
public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{
IDictionary<string, object> dictionary = tempDataProvider.LoadTempData(controllerContext);
this._data = (dictionary != null) ?
    new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase) :
    new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
this._initialKeys = new HashSet<string>(this._data.Keys, StringComparer.OrdinalIgnoreCase);
this._retainedKeys.Clear();
}

继续看LoadTempData()方法, 真相就能大白了.

//System.Web.Mvc.SessionStateTempDataProvider
public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
HttpSessionStateBase session = controllerContext.HttpContext.Session;
if (session != null)
{
Dictionary<string, object> dictionary = session["__ControllerTempData"] as Dictionary<string, object>;
if (dictionary != null)
{
session.Remove("__ControllerTempData");
return dictionary;
}
}
return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}

从这里能看到, 我所言非虚了, 确实是存放在Session中的.

注意 : 同一个 TempData 只能被传递一次, 当在Session中找到TempData后, 就会将它清除掉, 下一次请求是不能再获取到这个数据. 因为在Session中已经被清除了.

2. GetRequiredString("action")

这个方法应该不需要多说了, 跟前面获取控制器名称的方式一样, 只不过这里是用来获取 Action 方法的名称

3. InvokeAction 这个是重点方法

前面既然已经获取到 Action 方法的名字, 那么现在是不是应该去找到这个方法, 并看看是否能匹配的上呢? 匹配的时候, 用的是哪一些条件? 如果匹配的上, 又怎么执行的呢? 谜底即将揭晓.

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(actionName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
   //根据控制器上下文, 来获取控制器描述对象
ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
   //这里调用的FindAction方法, 其实就是调用控制器描述类里面的 FindAction 方法, 但是这是一个抽象方法
   //这里返回的是 Action 方法的描述信息
   //默认调用的是 System.Web.Mvc.ReflectedControllerDescriptor 的 FindAction 方法(这里说的都是同步的情况下)
ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor == null)
{
return false;
}
   //获取所有的过滤器
FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);
try
{
     //Authorization 过滤器, 执行之后,会将结果存放在 ActionResult 类型的Result属性中,
     //如果返回结果不为空, 则不会再去执行Action里面的方法和View里面的内容了.
AuthorizationContext context = this.InvokeAuthorizationFilters(controllerContext,
        filters.AuthorizationFilters, actionDescriptor);
if (context.Result != null)
{
this.InvokeActionResult(controllerContext, context.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
}
        //获取参数的信息, 存入字典中, 以供做参数匹配
IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
        //Action 过滤器, Action方法的执行也在这里面进行
ActionExecutedContext context2 = this.InvokeActionMethodWithFilters(controllerContext,
          filters.ActionFilters, actionDescriptor, parameterValues);
        //Result 过滤器
this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);
}
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception exception)
{
     //错误信息过滤器, HandleError
ExceptionContext context3 = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
if (!context3.ExceptionHandled)
{
throw;
}
this.InvokeActionResult(controllerContext, context3.Result);
}
return true;
}

乍一看, 里面的内容真心多啊. 还是那句话, 不要怕, 一个一个来.

3.1 FindAction - 先看一下这个方法, 是怎么找到 Action 的

//ReflectedControllerDescriptor
public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(actionName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
MethodInfo methodInfo = this._selector.FindActionMethod(controllerContext, actionName);
if (methodInfo == null)
{
return null;
}
   //ReflectedActionDescriptor 类是 继承自 ActionDescriptor类的
return new ReflectedActionDescriptor(methodInfo, actionName, this);
}

从这里看, 是通过控制器上下文和Action方法的名称去获取到的.

那么进去再看一下吧.

public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName)
{
List<MethodInfo> matchingAliasedMethods = this.GetMatchingAliasedMethods(controllerContext, actionName);
matchingAliasedMethods.AddRange(this.NonAliasedMethods[actionName]);
List<MethodInfo> ambiguousMethods = RunSelectionFilters(controllerContext, matchingAliasedMethods);
switch (ambiguousMethods.Count)
{
case :
return null; case :
return ambiguousMethods[];
}
throw this.CreateAmbiguousMatchException(ambiguousMethods, actionName);
}

3.1.1 GetMatchingAliaseMethods / NonAliaseMethods

这里可能有点让人费解, 可能是不明白 Aliased 的意思, 翻译过来其实是别名的意思.

这里牵涉到Action的一个功能, 就是给Action取别名. 通过 ActionNameAttribute 特性来进行.

这里的意思, 就是获取到所有的方法, 包括有别名的和没有别名的. 对于声明了 NonAliasedAttribute特性的方法, 也会获取出来.

3.1.2 RunSelectionFilters

private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos)
{
List<MethodInfo> list = new List<MethodInfo>();
List<MethodInfo> list2 = new List<MethodInfo>();
using (List<MethodInfo>.Enumerator enumerator = methodInfos.GetEnumerator())
{
Func<ActionMethodSelectorAttribute, bool> predicate = null;
MethodInfo methodInfo;
while (enumerator.MoveNext())
{
methodInfo = enumerator.Current;
ICollection<ActionMethodSelectorAttribute> actionMethodSelectorAttributes =
            ReflectedAttributeCache.GetActionMethodSelectorAttributes(methodInfo);
if (actionMethodSelectorAttributes.Count == )
{
list2.Add(methodInfo);
}
else
{
if (predicate == null)
{
predicate = attr => attr.IsValidForRequest(controllerContext, methodInfo);
}
if (actionMethodSelectorAttributes.All<ActionMethodSelectorAttribute>(predicate))
{
list.Add(methodInfo);
}
}
}
}
if (list.Count <= )
{
return list2;
}
return list;
}

这里其实是对方法进行一个过滤和一个返回优先级的问题.

Action上的Attribute如果是ActionMethodSelectorAttribute类型或者是继承了它的, 并且该特性的 IsValidForRequest()返回的结果是true,那么就会通过筛选, 优先返回.

这里其实主要是为了过滤 AcceptVerbsAttributes 和 NonActionAttribute 的.

NonActionAttribute : 他的IsValidForRequest()返回的是false, 所以Action上有此特性的方法会被筛选掉.

AcceptVerbsAttributes  : 同名方法, 必须声明不同的此特性, post, get, 否则也还是会报错.

3.2 GetFilters() - 获取Controller 和 Action 中, 声明的所有的 过滤器

protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return new FilterInfo(this._getFiltersThunk(controllerContext, actionDescriptor));
}

这里的_getFiltersThunk是一个Func<>()委托, 那么具体是什么方法呢?

其实这里指向的是System.Web.Mvc.FilterProviderCollection的GetFilters()方法, 别问我是怎么知道的哦.

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (actionDescriptor == null)
{
throw new ArgumentNullException("actionDescriptor");
}
IEnumerable<Filter> source = (from fp in this.CombinedItems
      select fp.GetFilters(controllerContext, actionDescriptor))
        .OrderBy<Filter, Filter>(filter => filter, _filterComparer);
return this.RemoveDuplicates(source.Reverse<Filter>()).Reverse<Filter>();
}

看一下这里的GetFilters(), 他其实是IFilterProvider接口中的方法, 那么哪一些类实现了这个接口呢?

解析到这里, 大致已经能知道, 获取了哪一些过滤器了.

在更进一步, 看一下 ControllerInstanceFilterProvider里面, 干了些什么.

public enum FilterScope
{
Action = ,
Controller = ,
First = ,
Global = ,
Last =
} public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext.Controller == null)
{
yield break;
}
yield return new Filter(controllerContext.Controller, FilterScope.First, -);
}

3.3 InvokeAuthorizationFilters() - Authorization过滤器, 控制器的部分下一篇会详细描述

protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, 
    IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext filterContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter filter in filters)
{
     //遍历执行控制器方法
filter.OnAuthorization(filterContext);
if (filterContext.Result != null)
{
return filterContext;
}
}
return filterContext;
}

3.4 InvokeActionMethodWithFilters() - Action 过滤器, 这里面其实会执行两个过滤器 : OnActionExecuting / OnActionExecuted

我们其实都知道, 这两个过滤器中间, 还少了一个东西, 就是 Action 方法的执行, 那么在执行这个方法的过程之中, 就会去执行 Action 方法

protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, 
  IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
Func<ActionExecutedContext> seed = () => new ActionExecutedContext(controllerContext, actionDescriptor, false, null)
    { Result = this.InvokeActionMethod(controllerContext, actionDescriptor, parameters) };
return filters.Reverse<IActionFilter>().Aggregate<IActionFilter,
    Func<ActionExecutedContext>>(seed, (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next))();
}

方法

参数

描述

OnActionExecuting

ActionExecutingContext

在行为方法执行前执行

OnActionExecuted

ActionExecutedContext

在行为方法执行后执行

OnResultExecuting

ResultExecutingContext

在行为方法返回前执行

OnResultExecuted

ResultExecutedContext

在行为方法返回后执行

过滤器这部分内容会在后面的篇章做出详细解释.

3.5 InvokeActionResultWithFilters() - Result 过滤器, 这里面也会执行两个过滤器 : OnResultExecuting / OnResultExecuted

这里同上面是一样的, 这两个过滤器中间, 少了一个 View 的执行, 也就是说, 在执行这个方法的时候, 会动态执行 View 页面方法.

protected virtual ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext, 
  IList<IResultFilter> filters, ActionResult actionResult)
{
ResultExecutingContext preContext = new ResultExecutingContext(controllerContext, actionResult);
Func<ResultExecutedContext> seed = delegate {
this.InvokeActionResult(controllerContext, actionResult);
return new ResultExecutedContext(controllerContext, actionResult, false, null);
};
return filters.Reverse<IResultFilter>().Aggregate<IResultFilter,
    Func<ResultExecutedContext>>(seed, (next, filter) => () => InvokeActionResultFilter(filter, preContext, next))();
}

3.6  InvokeActionResult()

注意到这里还有一个方法, 就是当过滤器不通过的时候执行的. 来看一下吧.

protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
{
actionResult.ExecuteResult(controllerContext);
}

从这里可能还不能比较直观的知道, 在做什么, 但是我贴一张图, 应该就能比较清楚了

4. PossiblySaveTempData()

internal void PossiblySaveTempData()
{
if (!base.ControllerContext.IsChildAction)
{
base.TempData.Save(base.ControllerContext, this.TempDataProvider);
}
}

这里是保存 TempData数据

二、扩展或功能

1. 给方法取别名

我这里使用的是前面讲路由的例子, 目录结构如下图:

[ActionName("Get")]
public ActionResult GetA(int id )
{
return View(id);
}

如果没有这个别名, 我这里肯定是找不到这个视图的, 请求的路径也应该是 Home/GetA的方式. 那么下面看一下结果:

从这里的结果来看, 我的请求路径是 Home/Get 方式

那如果我的别名和我的另一个方法相同, 那么就算我的参数并不一样, 但是会报错的. MVC 不知道该用哪一个方法了.

目录已同步

MVC源码分析 - Action查找和过滤器的执行时机的更多相关文章

  1. MVC源码分析 - Action/Result 过滤器执行时机

    前面 的篇章, 解析了Action方法的查找, 以及 Authorize, Action, Result, Error 过滤器的加载时机. 也花了两篇去看授权和错误过滤器的使用. 但是对于 Actio ...

  2. MVC源码分析 - Action/Result 过滤器(续)

    上一篇 看到了Action/Result过滤器的执行顺序: OnActionExecuting -> Action -> OnActionExecuted -> OnResultEx ...

  3. asp.net mvc源码分析-Action篇 IModelBinder

    我们首先还是看看ReflectedParameterBindingInfo的Binder属性吧: public override IModelBinder Binder {            ge ...

  4. 精尽Spring MVC源码分析 - 寻找遗失的 web.xml

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  6. ASP.NET MVC 源码分析(一)

    ASP.NET MVC 源码分析(一) 直接上图: 我们先来看Core的设计: 从项目结构来看,asp.net.mvc.core有以下目录: ActionConstraints:action限制相关 ...

  7. 精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  8. Struts2 源码分析——Action代理类的工作

    章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...

  9. WebForm / MVC 源码分析

    ASP.NET WebForm / MVC 源码分析   浏览器 Url:https//localhost:6565/Home/Index ,https//localhost:6565/WebForm ...

随机推荐

  1. Alpha冲刺 - (10/10)

    Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 本人负责的模块(共享编辑)的前端代码 ...

  2. POJ 1191棋盘分割问题

    棋盘分割问题 题目大意,将一个棋盘分割成k-1个矩形,每个矩形都对应一个权值,让所有的权值最小求分法 很像区间DP,但是也不能说就是 我们只要想好了一个怎么变成两个,剩下的就好了,但是怎么变,就是变化 ...

  3. python之随机验证码

    一.内置函数 chr ------把数字转换为字母 ord-------把字母转换为数字 n = chr(65) print (n) m = ord("A") print(m) 二 ...

  4. bootstrap4相关文档

    本节课我们主要学习一下Bootstrap的两个个组件功能:输入框组件和导航导航条组件. 一.输入框组件 文本输入框就是可以在<input>元素前后加上文字或按钮,可以实现对表单控件的扩 展 ...

  5. poj3253哈夫曼树

    Fence Repair Time Limit: 2000 MS Memory Limit: 65536 KB 64-bit integer IO format: %I64d , %I64u Java ...

  6. Android-Java控制多线程执行顺序

    功能需求: Thread-0线程:打印 1 2 3 4 5 6 Thread-1线程:打印1 1 2 3 4 5 6 先看一个为实现(功能需求的案例) package android.java; // ...

  7. Android-多线程安全问题-synchronized

    先看一个售票案例Demo,多线程程序对共享数据操作引发的安全问题: package android.java.thread09; /** * 售票线程 */ class Booking impleme ...

  8. [LintCode] Longest Increasing Continuous subsequence

    http://www.lintcode.com/en/problem/longest-increasing-continuous-subsequence/# Give you an integer a ...

  9. 背水一战 Windows 10 (48) - 控件(集合类): FlipView

    [源码下载] 背水一战 Windows 10 (48) - 控件(集合类): FlipView 作者:webabcd 介绍背水一战 Windows 10 之 控件(集合类) FlipView 示例Fl ...

  10. ceph 源码安装 configure: error: "Can't find boost spirit headers"

    问题:configure: error: "Can't find boost spirit headers" 解决: 推荐:sudo apt-get install libboos ...