zoukankan      html  css  js  c++  java
  • 简说MVC Filter

    Filter与FilterProvider之间的关系

    根据用途和执行时机的不同,MVC主要分为以下5种类型的过虑器:AuthenticationFilter、AuthorizationFilter、ActionFilter、ExceptionFilter、ResultFilter。下面我们来看一个IFilter接口,如下所示:

    public class Filter
    {
        public const int DefaultOrder = -1;
    
        public Filter(object instance, FilterScope scope, int? order)
        {
            //省略
            if (order == null)
            {
                IMvcFilter mvcFilter = instance as IMvcFilter;
                if (mvcFilter != null)
                {
                    order = mvcFilter.Order;
                }
            }
    
            Instance = instance;
            Order = order ?? DefaultOrder;
            Scope = scope;
        }
    
        public object Instance { get; protected set; }
    
        public int Order { get; protected set; }
    
        public FilterScope Scope { get; protected set; }
    }
    
    public enum FilterScope
    {
        First = 0,
        Global = 10,
        Controller = 20,
        Action = 30,
        Last = 100,
    }

    一个Filter对象就是对一个过滤器的封装,它将过滤器对象封装在Instance属性中。表示排序的Order属性,默认为-1,值越小越优先。还有一个表示范围的Scope属性,它是一个枚举类型。其中Global、Controller、Action分别表示应用到整个应用范围内、某个Controller上、某个Action上,另两个First、Last表明是应用的第一个还是最后一个。如果前面的Order属性的值一样大,那么相同的Order的Filter则会按照Scope属性值越小越优先。

    所有的Filter均是通过FilterProvider来提供的,该接口的定义如下所示:

    public interface IFilterProvider
    {
        IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
    }

     该接口只定义了一个GetFilters方法,用于获取某个Action方法上所有的Filter。它具有两个参数ControllerContext表示当前的Controller的上下文,ActionDescriptor描述目标Action方法。

    用于提供Filter的FilterProvider是通过静态类型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; }
    }

     从上我们看到MVC提供了三种原生的FilterProvider,它们分别是:GlobalFilters.Filters用于提供全局、FilterAttributeFilterProvider用于提供Controller类和Action方法上、ControllerInstanceFilterProvider用于提供Controller这个别特的过滤器。下面来看FilterProvider是如何提供Filter的。

    1、FilterAttributeFilterProvider

    我们通常是将过滤器定义成特性的方式标注到某个Controller或Action上。这样的过滤器特性一般以FilterAttribute作为基类。它实现了IMvcFilter接口,IMvcFilter只有两个只读属性成员Order和AllowMultiple。在上面的Filter类中我们看到如果实例可以转换为IMvcFilter接口,那么则将IMvcFilter的Order值赋给Filter的Order属性。我们在某个Action方法或Controller上标记某个特性时,可以在上面指定Order值,如[Authorize(Order =3)]。如下所示:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public abstract class FilterAttribute : Attribute, IMvcFilter
    {
        private static readonly ConcurrentDictionary<Type, bool> _multiuseAttributeCache = new ConcurrentDictionary<Type, bool>();
        private int _order = Filter.DefaultOrder;
    
        public bool AllowMultiple
        {
            get { return AllowsMultiple(GetType()); }
        }
    
        public int Order
        {
            get { return _order; }
            set
            {
                if (value < Filter.DefaultOrder)
                {
                    throw new ArgumentOutOfRangeException("value", MvcResources.FilterAttribute_OrderOutOfRange);
                }
                _order = value;
            }
        }
    
        private static bool AllowsMultiple(Type attributeType)
        {
            return _multiuseAttributeCache.GetOrAdd(
                attributeType,
                type => type.GetCustomAttributes(typeof(AttributeUsageAttribute), true)
                            .Cast<AttributeUsageAttribute>()
                            .First()
                            .AllowMultiple);
        }
    }
    
    public interface IMvcFilter
    {
        bool AllowMultiple { get; }
        int Order { get; }
    }
    从应用在FilterAttribute的AttributeUsageAttribute特性上我们可以看到此特性可以应用到类和方法上,AllowMultiple默认为false,不允许多个。

     获取Controller类和Action方法上的特性是通过FilterAttributeFilterProvider来提供的。如下所示:

    public class FilterAttributeFilterProvider : IFilterProvider
    {
        private readonly bool _cacheAttributeInstances;
    
        public FilterAttributeFilterProvider()
            : this(true)
        {
        }
    
        public FilterAttributeFilterProvider(bool cacheAttributeInstances)
        {
            _cacheAttributeInstances = cacheAttributeInstances;
        }
    
        protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return actionDescriptor.GetFilterAttributes(_cacheAttributeInstances);
        }
    
        protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return actionDescriptor.ControllerDescriptor.GetFilterAttributes(_cacheAttributeInstances);
        }
    
        public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            // Results are low in number in the common case so use yield return to avoid creating intermediate collections or nested enumerables
            if (controllerContext.Controller != null)
            {
                foreach (FilterAttribute attr in GetControllerAttributes(controllerContext, actionDescriptor))
                {
                    yield return new Filter(attr, FilterScope.Controller, order: null);
                }
                foreach (FilterAttribute attr in GetActionAttributes(controllerContext, actionDescriptor))
                {
                    yield return new Filter(attr, FilterScope.Action, order: null);
                }
            }             
        }
    }

     从上面的静态类FilterProviders中,我们可以看到MVC默认注册的是FilterAttributeFilterProvider的无参构造函数,然后它传一个true调用有参构造函数,该_cacheAttributeInstances字段表示是否缓存获取到的Filter。因为是通过反射获取Controller和Action上的Filter,所以MVC默认缓存获取到的Filter,提高性能。

    该类实现的GetFilters方法,它会先后调用GetControllerAttributes方法和GetActionAttributes方法,分别获取Cotroller和Action上Filter。分别用于描述 Controller和Action的ControllerDescriptor和ActionDescriptor类实现了ICustomAttributeProvider接口。我们可以调用相应的方法获取应用在对应的Controller类型或Action方法上包括FilterAttribute在内的所有特性。如下所示:

    public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
    {
        public virtual object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            if (attributeType == null)
            {
                throw new ArgumentNullException("attributeType");
            }
    
            return (object[])Array.CreateInstance(attributeType, 0);
        }
    
        public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
        {
            return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
        }
    }
    
    public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
    {
        public virtual object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            if (attributeType == null)
            {
                throw new ArgumentNullException("attributeType");
            }
    
            return (object[])Array.CreateInstance(attributeType, 0);
        }
    
        public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
        {
            return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
        }
    }

    2、ControllerInstanceFilterProvider

    Controller本身就是一个过滤器,如下所示:

    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
    {
        //省略
    }

    对于Controller这个特殊的过滤器,其对应的FilterProvider类型,如下所示:

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

    它的GetFilter方法跟据ControllerContext获取对应的Controller对象,并以此创建对应的Filter。

     3、GlobalFilters.Filters

    全局过滤器,它不是显示的应用于某个Controller、某个Action上,而是默认应用到整个程序的所有Controller类型的Action方法上。如下所示:

    public static class GlobalFilters
    {
        static GlobalFilters()
        {
            Filters = new GlobalFilterCollection();
        }
    
        public static GlobalFilterCollection Filters { get; private set; }
    }
    GlobalFilters就只有一个属性GlobalFilterCollection,GlobalFilterCollection它是一个元素类型为Filter的集合,它显示地实现了IFilterProvider接口的GetFilters方法,返回的就是它自己,如下所示:
    public sealed class GlobalFilterCollection : IEnumerable<Filter>, IFilterProvider
    {
        private List<Filter> _filters = new List<Filter>();
        
        public void Add(object filter)
        {
            AddInternal(filter, order: null);
        }
    
        public void Add(object filter, int order)
        {
            AddInternal(filter, order);
        }
        
        private void AddInternal(object filter, int? order)
        {
            ValidateFilterInstance(filter);
            _filters.Add(new Filter(filter, FilterScope.Global, order));
        }
        
        IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return this;
        }
      //省略
    }

    到目前为止,MVC默认提供的3种原生的FilterProvider以及各自采用的Filter提供机制介绍完毕。MVC的5种过滤器最终被封装成相应的Filter对象,但是它们的执行时机和方式是不同的,所以在执行之前需要根据被封装的过滤器类型对所有的Filter进行分组。就是当ControllerActionInvoker被调用时,它会利用静态类型FilterProviders得到所有注册的IFilterProvider类型,然后利用当前的ControllerContext和ActionDescriptor对象得到Filter,然后根据其Instance属性表示的过滤器类型,将它分组,最终得到一个具有如下所示FilterInfo类型的对象。

    public class FilterInfo
    {
        public IList<IActionFilter> ActionFilters { get; }
    
        public IList<IAuthenticationFilter> AuthenticationFilters { get; }
    
        public IList<IAuthorizationFilter> AuthorizationFilters { get; }
    
        public IList<IExceptionFilter> ExceptionFilters { get; }
    
        public IList<IResultFilter> ResultFilters { get; }
    }
  • 相关阅读:
    转:ITIL的開源軟件
    转:Asp.Net大型项目实践
    转:Ubuntu上apache多端口配置虚拟主机的方法
    转:JS 获取鼠标位置
    转:一切整合分享到新浪网易微博代码
    转:Facebook是如何发布代码的
    转:利用 Amazon Web Services 集成企业应用程序使用 Amazon SQS 发送 XML 消息
    sqlite3 常用操作
    转:4 款消息队列软件产品大比拼
    SQL Server 2008 列转行 实例
  • 原文地址:https://www.cnblogs.com/koeltp/p/7427106.html
Copyright © 2011-2022 走看看