• asp.net mvc源码分析Action篇 Filter


    紧接着上篇 asp.net mvc源码分析-Controllerl篇 ControllerDescriptor 现在我们该看  FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);这句代码了,意思很好明白就是获取当前的FilterInfo信息,而该方法非常简单就一句return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));

    首先我们来看看_getFiltersThunk是个上面东西:

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

    意思是根据当前的ControllerContext和ActionDescriptor来获取所有的Filter实例,这里提到一个 FilterProviders.Providers东东,

     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;
            }
        }

    看看mvc默认就提供了这3个FilterProvider,GlobalFilters.Filters没什么说,意思就是注册全局的Filiter处理,在我们默认的Global.asax.cs的Application_Start()中有这个一句

     RegisterGlobalFilters(GlobalFilters.Filters);所以GlobalFilters.Filters很好明白,接下来我们看看  Providers.Add(new ControllerInstanceFilterProvider())

        public class ControllerInstanceFilterProvider : IFilterProvider {
            public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
                if (controllerContext.Controller != null) {
                    // Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
                    yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
                }
            }
        }

    这个ControllerInstanceFilterProvider 非常特殊,我们可以不用管,之所以会有这个东东,是因为Controller实现了 IActionFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,如果没有这个ControllerInstanceFilterProvider 那么Controller实现这些接口就没有意义了,因为相应的方法没法调用。

    而FilterAttributeFilterProvider的实现相对比较复杂一点,但是很好理解,主要获取当前Controller和Action的FilterAttribute实例,还记得上篇而文章提到的ReflectedAttributeCache里面有ConcurrentDictionary<MethodInfo, ReadOnlyCollection<FilterAttribute>> _methodFilterAttributeCache和ConcurrentDictionary<Type, ReadOnlyCollection<FilterAttribute>> _typeFilterAttributeCache这两个东西吗?

    现在我们来看看Filter的构造函数,里面有这么几句需要我们注意

      if (order == null) {
                    IMvcFilter mvcFilter = instance as IMvcFilter;
                    if (mvcFilter != null) {
                        order = mvcFilter.Order;
                    }

                }
                Order = order ?? DefaultOrder;

            Scope = scope;

    当我们的Filter是通过ControllerInstanceFilterProvider来创建的话它的order不为null,所以我们不用考虑,而Scope =Global

    如果Filter是用过GlobalFilters.Filters来创建的话,那就看在添加是是否有order,没有order的话,就看当前实例是否实现了IMvcFilter,实现了的话就是接口的order,没有则是-1;而Scope =First

    如果是通过FilterAttributeFilterProvider来创建的话,它进来的order是null,但是FilterAttribute是实现了IMvcFilter接口的,所以它的order是默认是Filter.DefaultOrder (-1),而scope是Controller和Action,主要看属性是在Controller上还是在Action上。

    这些Filter集合中,是有一定的顺序的,先按照order从小到大排序,如果order相同就按Scope从小到大排序,最后去掉重复的

    现在我们来看看FilterInfo的够着函数了:

     public FilterInfo(IEnumerable<Filter> filters) {
                // evaluate the 'filters' enumerable only once since the operation can be quite expensive
                var filterInstances = filters.Select(f => f.Instance).ToList();

                _actionFilters.AddRange(filterInstances.OfType<IActionFilter>());
                _authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());
                _exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());
                _resultFilters.AddRange(filterInstances.OfType<IResultFilter>());
            }

    根据当前的filiter集合,把它们分别放到不同filter接口集合中。

    下面  AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext,filterInfo.AuthorizationFilters, actionDescriptor);
                        if (authContext.Result != null) {
                            // the auth filter signaled that we should let it short-circuit the request
                            InvokeActionResult(controllerContext, authContext.Result);
                        }

    几句就很好明白了,对此次请求做身份验证,如果没有通过则返回一个认证失败。这个东西很重要尤其是在权限设计的时候。

    验证 成功后继续执行

      if (controllerContext.Controller.ValidateRequest) {
                                ValidateRequest(controllerContext);
                            }

    Controller.ValidateRequest默认值为true,即是需要验证客户端请求的,

      if (controllerContext.IsChildAction) {return;}
    ValidationUtility.EnableDynamicValidation(HttpContext.Current);
    controllerContext.HttpContext.Request.ValidateInput();

    具体的验证内容很多,很麻烦,这里就忽略它吧。

    说了这么多我们来总结一下吧:如果我们要给整个应用程序添加一些处理,可以通过GlobalFilters.Filters来添加,如果需要给某个Controller类或则是Action添加特殊处理,可以通过FilterAttributeFilterProvider来添加。在我们实际开发中往往用到的是ActionFilterAttribute

       public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter

    当然如果有特殊的需求我们可以添加自己的FilterProvider,如在Application_Start()中可以写成 FilterProviders.Providers.Add(xxxxx);

    接下来我我们看看这些Filter事这么调用的,凭我们的直觉调用顺序应该是

    OnActionExecuting
    Action ......真正调用Action
    OnActionExecuted

    OnResultExecuting
    Result....真正调用Result
    OnResultExecuted

    而剩下的代码就只有这 

     IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
      ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
         InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);几句了,

     IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);这个方法是用来获取Action参数对应的值,实现很复杂我们计划放到后面在将。

    看到InvokeActionMethodWithFilters这个方法我们就知道实现的功能是调用Action和IActionFilter的两个方法:

            protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
                ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
                Func<ActionExecutedContext> continuation = () =>
                    new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
                        Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
                    };
    
                // need to reverse the filter list because the continuations are built up backward
                Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
                    (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
                return thunk();
            }
    

      这段代码我看了很久才看懂它的细节。 ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);这句就没什么说的了,简单实例化一个ActionExecutingContext对象,

      Func<ActionExecutedContext> continuation = () =>
                    new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
                        Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
                    };

    这句有个方法continuation 没有输入参数返回ActionExecutedContext实例,只是这个实例有一个Result 属性,它的之就是我们调用Action后的返回值(ActionResult类型)。

      Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
                    (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
                return thunk();

    这里关键是Aggregate的用法,continuation是一个初始值,将每次 InvokeActionMethodFilter(filter, preContext, next));的结果赋给continuation值,InvokeActionResultFilter的代码大致如下:

       internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
                filter.OnResultExecuting(preContext);
                bool wasError = false;
                ResultExecutedContext postContext = null;
                postContext = continuation();
                filter.OnResultExecuted(postContext);
                return postContext;
            }

    这里个方法没有修改continuation的值,当filter循环执行  filter.OnResultExecuting(preContext)完了的时候就开始调用continuation方法了,在执行了Action,再次循环filter 以执行 filter.OnResultExecuted(postContext);

    总之这段代码写的很难看懂,我个人认为拆开写要好些

    foreach(Filiter ,...)
    {
      filter.OnResultExecuting(preContext);
    }
     postContext = continuation();
    foreach(Filiter ,...)
    {
       filter.OnResultExecuted(postContext);
    }

    我当时为了理解这段代码自己写了一个语法测试

      class Program  
        {  
            static void Main(string[] args)  
            {  
                List<string> users = new List<string> { "majiang", "luyang", "xieyichuan" };  
                Func<string> seed = () => "First";  
                Func<Func<string>, string, Func<string>> next = (fun, str) => delegate()  
                {  
                    Console.WriteLine(str);  
                    var temp = fun();  
                    temp += "-" + str;  
                    Console.WriteLine(temp);  
                    return temp;  
                };  
                Func<string> result = users.Aggregate(seed, next);  
                result();  
                Console.ReadLine();  
            }  
      
      
        }  
        static class Helper  
        {  
            public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)  
            {  
      
                TAccumulate local = seed;  
                foreach (TSource local2 in source)  
                {  
                    local = func(local, local2);  
                }  
                return local;  
            }  
      
        }  
    

     剩下的InvokeActionResultWithFilters方法和这里的InvokeActionMethodWithFilters方法调用一样

  • 相关阅读:
    HttpClient
    Android子线程访问网络
    PhoneURLConnectGEt
    PhoneHttpGet
    PhoneNote
    SQLite
    书单
    通过Web预测网页出版日期的学习
    LeetCode-Maximum Subarray[dp]
    LeetCode-Triangle[dp]
  • 原文地址:https://www.cnblogs.com/majiang/p/2762987.html
Copyright © 2020-2023  润新知