zoukankan      html  css  js  c++  java
  • 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方法调用一样

  • 相关阅读:
    “XXXXX” is damaged and can’t be opened. You should move it to the Trash 解决方案
    深入浅出 eBPF 安全项目 Tracee
    Unity3d开发的知名大型游戏案例
    Unity 3D 拥有强大的编辑界面
    Unity 3D物理引擎详解
    Unity 3D图形用户界面及常用控件
    Unity 3D的视图与相应的基础操作方法
    Unity Technologies 公司开发的三维游戏制作引擎——Unity 3D
    重学计算机
    windows cmd用户操作,添加,设备管理员组,允许修改密码
  • 原文地址:https://www.cnblogs.com/majiang/p/2762987.html
Copyright © 2011-2022 走看看