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

来源:互联网 发布:c语言面试题 编辑:程序博客网 时间:2024/06/10 17:30

紧接着上篇 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(newControllerInstanceFilterProvider())

    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方法调用一样

原创粉丝点击