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

1、前言缓存

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

2、几种常见的Filter框架

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

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

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

3、源码分析源码分析

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

    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方法内,就是根据这个分类,在各个时机进行相应的调用的。

4、总结

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

相关文章
相关标签/搜索