一、前言

之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多 人觉得平时根本不需要知道这些,会用就行了。其实阅读源码是个很好的习惯,它不只停留在知道怎么用的阶段,而是让我们知道一系列的为什么,为什么这样设 计,为什么这样使用…。很多朋友应该看过《asp.net x 框架揭秘》这本书,确实不错,特别是边看源码边看书,可以有不小的收获。Ok,我不是大神,我只是心血来潮想看一下源码!

二、几种常见的Filter

说到mvc里的Filter,自然会想到 IAuthorizationFilter,IActionFilter,IResultFilter,IExceptionFilter,搜索一下也都 知道怎么用了。其实说白了,这些接口定义了一系列方法,这些方法在请求的不同时机被执行,所谓Filter,就是让我们可以在不同时机进行拦截处理。

这里还涉及到一个特性:FilterAttribute,例如常用的AuthorizeAttribute就继 承了FilterAttribute和实现了IAuthorizationFilter接口。说到Attribute,马上会关联到:运行时、反射、性 能。框架会在运行过程中,通过反射获取标记属性,并执行特定的操作;至于性能问题,通常可以通过缓存来优化。

所以,我们可以做出猜测,以AuthorizeAttribute为例,msdn说它可以进行权限验证,也就是 在Action执行前,框架会通过反射获取标记在Action(或Controller)上的FilterAttribute,并执行 IAuthorizationFilter定义的OnAuthorization方法,在该方法内部进行权限验证。所以如果我们要在Action执行前做 某些判断或处理,可以 1.定义一个Attribute继承FilterAttribute,并实现IActionFilter接口(与 IAuthorizationFilter不同的是,这个时候ModelBinding已经完成);2.实现IActionFilter中的方法;3.标 记在Action(或Controller上)。ok,下面就通过源码来验证这个过程。

三、源码分析

Action的执行是由ActionInvoker负责的,我们直接从这里出发。IActionInvoker定义了ActionInvoker要实现的方法,该接口定义如下:

public interface IActionInvoker
{
    bool InvokeAction(ControllerContext controllerContext, string actionName);
}

ControllerActionInvoker  实现了该接口,顾名思义,它用于执行Controller 的 Action方法。它的 InvokeAction如下:

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
    ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
    ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
    if (actionDescriptor != null)
    {
        //标记1
        FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);         try
        {
            //标记2
            AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);             if (authenticationContext.Result != null)
            {
                InvokeActionResult(controllerContext, authenticationContext.Result);
            }
            else
            {
                IPrincipal principal = authenticationContext.Principal;                 if (principal != null)
                {
                    Thread.CurrentPrincipal = principal;
                    HttpContext.Current.User = principal;
                }                 //标记3
                AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
                if (authorizationContext.Result != null)
                {
                    AuthenticationChallengeContext challengeContext =
                        InvokeAuthenticationFiltersChallenge(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor, authorizationContext.Result);                          
                    InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
                }
                else
                {
                    if (controllerContext.Controller.ValidateRequest)
                    {
                        ValidateRequest(controllerContext);
                    }                     //标记4
                    IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
                    ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
                    //标记5
                    InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
                }
            }
        }
        catch (Exception ex)
        {
            //标记6
            ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
            if (!exceptionContext.ExceptionHandled)
            {
                throw;
            }
            InvokeActionResult(controllerContext, exceptionContext.Result);
        }         return true;
    }
    return false;
}

其实这里的6个标记已经印证了我们的猜测,先获取各种Filter,然后在各个时机执行它们。上面标记2-6都是InvokeXXXFilters就是具体的执行方法。

但是,到这里上面我们说到的FilterAttribute还没有出现。我们先把焦点放到标记1,GetFilters 上,它获取一个FilterInfo。GetFilters的定义如下:

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

_getFiltersThunk 是一个私有变量:

private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = FilterProviders.Providers.GetFilters;

通过定义可以看出,_getFiltersThunk 会返回一个Filter 集合(这里的Filter是一个实际的类,而上面提到的是概念性的东西,或者叫过滤器更合适),Filter 对象包装了IXXXFilter接口对象,具体是在其Instance 属性中。这里有点绕,但不影响,简单的说就是 GetFilters 方法会根据 FilterProviders.Providers.GetFilters 返回的一个IEnumerable<Filter>包装一个 FilterInfo对象。

我们先看 IEnumerable<Filter> 是如何获取的,它通过 FilterProviders.Providers.GetFilters 获得,FilterProviders 定义如下:

public static class FilterProviders
{
static FilterProviders()
{
Providers = new FilterProviderCollection();
Providers.Add(GlobalFilters.Filters);
Providers.Add(new FilterAttributeFilterProvider());
Providers.Add(new ControllerInstanceFilterProvider());
} public static FilterProviderCollection Providers { get; private set; }
}

这里可以注册自定义的FilterProvider,FilterProvider实际是实现了IFilterProvider(定义了 GetFilters方法)的类。可以看到,mvc 默认已经准备两个FilterProvider。调用GetFilters实际会遍历每一个FilterProvider的GetFilters方法,以 内置的FilterAttributeFilterProvider 为例,它的 GetFilters方法如下:

public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
ControllerBase controller = controllerContext.Controller;
var typeFilters = GetControllerAttributes(controllerContext, actionDescriptor)
.Select(attr => new Filter(attr, FilterScope.Controller, null));
var methodFilters = GetActionAttributes(controllerContext, actionDescriptor)
.Select(attr => new Filter(attr, FilterScope.Action, null)); return typeFilters.Concat(methodFilters).ToList();
}

这里也可以看到,Filter对象包装了具体的过滤器。其中GetControllerAttributes,实际它会调用ControllerDescriptor的 GetFilterAttribute,该方法定义如下:

public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
{
return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
}

ok,FilterAttribute 终于出现了!GetActionAttributes 也是类似的过程。

获取到Controller和Action的FilterAttribute,并包装成Filter集合后,就会构建一个FilterInfo对象,该对象的作用可以从其构造函数看出:

public FilterInfo(IEnumerable<Filter> filters)
{
// evaluate the 'filters' enumerable only once since the operation can be quite expensive
var cache = filters.ToList(); var overrides = cache.Where(f => f.Instance is IOverrideFilter); FilterScope actionOverride = SelectLastScope<IActionFilter>(overrides);
FilterScope authenticationOverride = SelectLastScope<IAuthenticationFilter>(overrides);
FilterScope authorizationOverride = SelectLastScope<IAuthorizationFilter>(overrides);
FilterScope exceptionOverride = SelectLastScope<IExceptionFilter>(overrides);
FilterScope resultOverride = SelectLastScope<IResultFilter>(overrides); _actionFilters.AddRange(SelectAvailable<IActionFilter>(cache, actionOverride));
_authenticationFilters.AddRange(SelectAvailable<IAuthenticationFilter>(cache, authenticationOverride));
_authorizationFilters.AddRange(SelectAvailable<IAuthorizationFilter>(cache, authorizationOverride));
_exceptionFilters.AddRange(SelectAvailable<IExceptionFilter>(cache, exceptionOverride));
_resultFilters.AddRange(SelectAvailable<IResultFilter>(cache, resultOverride));
}

很明显,它将Filter 按照各自IXXXFilter接口进行分类。在InvokeAction方法内,就是根据这个分类,在各个时机进行相应的调用的。

四、总结

通过一张图来简单总结一下:

ASP.NET MVC 几种 Filter 的执行过程源码解析的更多相关文章

  1. 通过源码了解ASP.NET MVC 几种Filter的执行过程 在Winform中菜单动态添加“最近使用文件”

    通过源码了解ASP.NET MVC 几种Filter的执行过程   一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神 ...

  2. 通过源码了解ASP.NET MVC 几种Filter的执行过程

    一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源 ...

  3. 了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult & JsonResult

    在之前的两篇文章(<EmptyResult & ContentResult>和<FileResult>)我们剖析了EmptyResult.ContentResult和F ...

  4. 返璞归真 asp.net mvc (5) - Action Filter, UpdateModel, ModelBinder, Ajax, Unit Test

    原文:返璞归真 asp.net mvc (5) - Action Filter, UpdateModel, ModelBinder, Ajax, Unit Test [索引页] [源码下载] 返璞归真 ...

  5. ASP.NET MVC学习笔记-----Filter

    ASP.NET MVC学习笔记-----Filter(1) Filter类型 接口 MVC的默认实现 Description Authorization IAuthorizationFilter Au ...

  6. ASP.NET MVC学习笔记-----Filter(2)

    接上篇ASP.NET MVC学习笔记-----Filter(1) Action Filter Action Filter可以基于任何目的使用,它需要实现IActionFilter接口: public ...

  7. ASP.NET MVC 5使用Filter过滤Action参数防止sql注入,让你代码安全简洁

    在开发程序的过程中,稍微不注意就会隐含有sql注入的危险.今天我就来说下,ASP.NET mvc 5使用Filter过滤Action参数防止sql注入,让你代码安全简洁.不用每下地方对参数的值都进行检 ...

  8. Asp.net MVC集成Google Calendar API(附Demo源码)

    Asp.net MVC集成Google Calendar API(附Demo源码) Google Calendar是非常方便的日程管理应用,很多人都非常熟悉.Google的应用在国内不稳定,但是在国外 ...

  9. ASP.NET MVC的Action Filter

    一年前写了一篇短文ASP.NET MVC Action Filters,整理了Action Filter方面的资源,本篇文章详细的描述Action Filter.Action Filter作为一个可以 ...

随机推荐

  1. xamarin SimpleAdapter绑定出错问题

    问题:今天在实验xamarin中SimpleAdapter绑定到ListView时,出现闪退的现象, 见图: 解决方法: SimpleAdapter中的构造函数public SimpleAdapter ...

  2. [BZOJ - 2463] [中山市选2009] 谁能赢呢?【“博弈论”】

    题目链接:BZOJ - 2463 题目分析 这道题的题解是,由于两人都采取最优策略,所以最后一定所有格子都会被走到.(Why..表示不懂..哪位神犇可以给我讲一下QAQ) Upd:半群的神犇告诉我,并 ...

  3. windows7+iis7+php的配置

    最近在找工作,人被逼了,所以没事就学习php了.以下是开发环境的搭建: 环境搭建 然后就是解析php脚本的两种配置方法: fastCgiModule与ISAPI方式 两种配置方法 本文为转载...

  4. 【算法Everyday】第三日 KMP算法

    题目 你知道的. 分析 分析不来. 代码 void OutputArray(int* pArr, int iLen) { ; i < iLen; i++) { printf("%d\t ...

  5. 字符串(后缀自动机):USACO Dec10 恐吓信

    [题目描述] FJ刚刚和邻居发生了一场可怕的争吵,他咽不下这口气,决定佚名发给他的邻居一封脏话连篇的信.他有无限张完全相同的已经打印好的信件,都包含 N个字母(1<=N<=50,000). ...

  6. 【模拟】【数学】CSU 1803 2016 (2016湖南省第十二届大学生计算机程序设计竞赛)

    题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1803 题目大意: 给定n,m(n,m<=109)1<=i<=n,1& ...

  7. 【枚举】Vijos P1496 火柴棒等式 (NOIP2008提高组第二题)

    题目链接: https://vijos.org/p/1496 题目大意: 给你n(n<24)根火柴棍,你可以拼出多少个形如“A+B=C”的等式?("+"和"=&qu ...

  8. Partition List ——LeetCode

    Given a linked list and a value x, partition it such that all nodes less than x come before nodes gr ...

  9. cf703A Mishka and Game

    A. Mishka and Game time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  10. [Locked] Count Univalue Subtrees

    Count Univalue Subtrees Given a binary tree, count the number of uni-value subtrees. A Uni-value sub ...