簡說MVC Filter

Filter與FilterProvider之間的關係緩存

根據用途和執行時機的不一樣,MVC主要分爲如下5種類型的過慮器:AuthenticationFilter、AuthorizationFilter、ActionFilter、ExceptionFilter、ResultFilter。下面咱們來看一個IFilter接口,以下所示:mvc

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屬性值越小越優先。ide

 

全部的Filter均是經過FilterProvider來提供的,該接口的定義以下所示:函數

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

 該接口只定義了一個GetFilters方法,用於獲取某個Action方法上全部的Filter。它具備兩個參數ControllerContext表示當前的Controller的上下文,ActionDescriptor描述目標Action方法。性能

 

用於提供Filter的FilterProvider是經過靜態類型FilterProviders來註冊的。以下所示:
this

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的。spa

一、FilterAttributeFilterProvider

咱們一般是將過濾器定義成特性的方式標註到某個Controller或Action上。這樣的過濾器特性通常以FilterAttribute做爲基類。它實現了IMvcFilter接口,IMvcFilter只有兩個只讀屬性成員Order和AllowMultiple。在上面的Filter類中咱們看到若是實例能夠轉換爲IMvcFilter接口,那麼則將IMvcFilter的Order值賦給Filter的Order屬性。咱們在某個Action方法或Controller上標記某個特性時,能夠在上面指定Order值,如[Authorize(Order =3)]。以下所示:code

[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,提升性能。blog

該類實現的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>();
    }
}

 

二、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。

 三、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; }
}
相關文章
相關標籤/搜索