ASP.NET MVC 필터에 대한 자세한 설명

高洛峰
풀어 주다: 2016-12-24 13:32:58
원래의
1093명이 탐색했습니다.

ActionInvoker에 의한 Action 실행 중에는 ActionDescriptor를 이용한 Action 메소드 실행과 이전의 Model 바인딩 및 검증 외에 중요한 작업이 있는데, 바로 관련 필터(Filter)이다. 실행. ASP.NET MVC의 필터는 AOP(관점 지향 프로그래밍)를 기반으로 한 설계입니다. 해당 필터에 일부 비비즈니스 로직을 구현한 다음 이를 크로스커팅 방식으로 해당 필터에 적용합니다. 이러한 필터는 Action 메서드가 실행되기 전후에 자동으로 실행됩니다. ASP.NET MVC는 해당 필터 인터페이스(IAuthorizationFilter, IActionFilter, IResultFilter 및 IExceptionFilter)에 해당하는 네 가지 유형의 필터(AuthorizationFilter, ActionFilter, ResultFilter 및 ExceptionFilter)를 제공합니다. [이 글은 "ASP.NET MVC 작동 방식"에 동기화되었습니다.]

디렉토리

1. Filter

2. FilterProvider

3. FilterAttribute 및 FilterAttributeFilterProvider

4. Controller 및 ControllerInstanceFilterProvider

5. GlobalFilterCollection

6. 예제 데모: 필터의 제공 메커니즘 및 실행 순서 확인

1. 필터

ASP.NET MVC에서 제공하는 4가지 유형의 필터에는 자체적으로 구현된 인터페이스가 있지만 필터 제공 시스템의 경우 모든 필터는 다음 정의와 함께 Filter 유형으로 표시됩니다. Filter의 핵심은 Instance 속성입니다. 왜냐하면 이 객체는 실제로 필터링 기능을 구현하는 객체를 나타내기 때문입니다. 이 객체는 위의 네 가지 필터 유형을 기반으로 하나 이상의 인터페이스를 구현합니다.

public class Filter
{ 
  public const int DefaultOrder = -1; 
  public Filter(object instance, FilterScope scope, int? order);
   
  public object Instance { get; protected set; }
  public int Order { get; protected set; }
  public FilterScope Scope { get; protected set; }
}
public enum FilterScope
{
  Action    = 30,
  Controller  = 20,
  First     = 0,
  Global    = 10,
  Last     = 100
}
로그인 후 복사


참고: System.Web.Mvc.Filter 및 IAuthorizationFilter를 구현하는 유형, IActionFilter, IResultFilter 및 IExceptionFilter를 모두 호출할 수 있으므로 "Filter"는 혼동을 피하기 위해 별도의 설명 없이 영어 "Filter"와 중국어 "Filter"를 각각 사용합니다.

필터의 Order 및 Scope 속성에 따라 궁극적으로 필터의 실행 순서가 결정됩니다. Order 속성의 해당 값이 작을수록 실행 우선순위가 높아집니다. 이 속성의 기본값은 -1입니다(Filter에 정의된 상수 DefaultOrder에 해당). 두 필터의 Order 속성 값이 동일한 경우 Scope 속성은 궁극적으로 어느 필터가 먼저 실행되는지 결정합니다. Filter의 Scope 속성 유형은 FilterScope 유형의 열거형입니다. 이 열거형은 필터 적용 범위를 나타냅니다. Action 및 Controller는 Action 메서드를 나타내고, First 및 Last는 첫 번째 및 마지막 Filter로 실행될 것으로 예상됨을 의미합니다.

위의 코드 조각을 통해 FilterScope의 5가지 열거 옵션 각각이 값으로 설정되어 있음을 알 수 있습니다. 이 값은 더 작은 열거 값이 먼저 실행되도록 결정합니다. FilterScope의 정의에서 동일한 Order 속성 값을 가진 여러 필터의 경우 컨트롤러에 적용된 필터가 Action 메서드에 적용된 필터보다 실행 우선순위가 더 높다는 결론을 도출할 수 있으며 전역 필터의 실행은 다음과 같습니다. 우선 순위는 작업 기반 필터보다 높습니다.

2. FilterProvider

Filter의 제공 메커니즘은 앞서 소개한 ModelBinder, ModelValidator 기반의 제공 메커니즘과 유사하며, 해당 Provider를 통해 제공됩니다. 필터를 제공하는 FilterProvider는 다음 코드 조각에 표시된 대로 IFilterProvider 인터페이스를 구현합니다. 이 인터페이스는 지정된 Controller 컨텍스트와 대상 작업을 설명하는 데 사용되는 ActionDescriptor 개체를 기반으로 필터 개체 컬렉션을 가져오는 유일한 메서드 GetFilters를 정의합니다.

public interface IFilterProvider
{ 
  IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}
로그인 후 복사


정적 유형의 FilterProvider를 통해 현재 애플리케이션에서 사용하는 FilterProvider를 등록하거나 가져올 수 있습니다. 다음 코드 조각에 표시된 대로 FilterProviders에는 전체 웹 응용 프로그램 내에서 사용되는 FilterProviders 목록을 나타내는 FilterProviderCollection 유형의 읽기 전용 속성 Providers가 있습니다. FilterProviderCollection은 요소 유형이 IFilterProvider인 컬렉션입니다. GetFilters 메서드는 컬렉션에 있는 모든 FilterProvider 개체 또는 제공되는 Filter 개체에 사용됩니다.

public static class FilterProviders
{ 
  public static FilterProviderCollection Providers { get; }
}
public class FilterProviderCollection : Collection<IFilterProvider>
{  
  //其他成员
  public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); 
}
로그인 후 복사


ASP.NET MVC는 FilterAttributeFilterProvider, ControllerInstanceFilterProvider 및 GlobalFilterCollection이라는 세 가지 기본 FilterProvider를 제공합니다. 다음으로 별도로 소개하겠습니다.

3. FilterAttribute 및 FilterAttributeFilterProvider

일반적으로 필터는 선언적 방식으로 Controller 유형이나 Action 메서드에 적용되는 속성으로 정의되며 추상 유형 FilterAttribute는 모든 필터의 기본 클래스입니다. 아래 코드 조각에 표시된 것처럼 FilterAttribute 특성은 IMvcFilter 인터페이스를 구현합니다. 이 인터페이스는 필터의 실행 순서를 제어하고 동일한 유형의 여러 필터를 적용하는 데 사용되는 Order 및 AllowMultiple이라는 두 가지 읽기 전용 특성을 정의합니다. 동시에 동일한 필터. 대상 요소(클래스 또는 메소드).

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)]
public abstract class FilterAttribute : Attribute, IMvcFilter
{ 
  protected FilterAttribute();
   
  public bool AllowMultiple { get; }
  public int Order { get; set; }
}
public interface IMvcFilter
{ 
  bool AllowMultiple { get; }
  int Order { get; }
}
로그인 후 복사


从应用在FilterAttribute上的AttributeUsageAttribute的定义可以看出该特性可以应用在类型和方法上,这意味着筛选器一般都可以应用在Controller类型和Action方法上。只读属性AllowMultiple实际上返回的是AttributeUsageAttribute的同名属性,通过上面的定义我们可以看到默认情况下该属性值为False。

用于描述Controller和Action的ControllerDescriptor和ActionDescriptor均实现了ICustomAttributeProvider接口,我们可以直接利用它们获取应用在对应的Controller类型或者Action方法上包括FilterAttribute在内的所有特性。实际上,这两个描述类型提供了单独的方法GetFilterAttributes专门用于获取FilterAttribute特性列表。如下面的代码片断所示,该方法具有一个布尔类型的参数useCache,表示是否需要对解析出来的FilterAttribute特性进行缓存以缓解频繁的反射操作对性能造成的影响。

public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
{
  //其他成员
  public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);
}
public abstract class ActionDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable
{ 
  //其他成员
  public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache); 
}
로그인 후 복사


针对FilterAttribute特性的Filter通过FilterAttributeFilterProvider对象来提供。FilterAttributeFilterProvider直接调用当前ControllerDescriptor和ActionDescriptor的GetFilterAttributes方法获取所有应用在Controller类型和当前Action方法的FilterAttribute特性,并借此创建相应的Filter对象。FilterAttributeFilterProvider构造函数的参数cacheAttributeInstances表示是否启用针对FilterAttribute的缓存,它将作为调用GetFilterAttributes方法的参数。在默认的情况下(通过调用默认无参的构造函数创建的FilterAttributeFilterProvider)会采用针对FilterAttribute的缓存。

public class FilterAttributeFilterProvider : IFilterProvider
{
  public FilterAttributeFilterProvider();
  public FilterAttributeFilterProvider(bool cacheAttributeInstances);
  protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
  protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
  public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}
로그인 후 복사


对于通过调用GetFilters得到的Filter,对应的FilterAttribute特性作为其Instance属性。Order属性来源于FilterAttribute的同名属性,而Scope属性则取决于FilterAttribute特性是应用在Controller类型上(Scope属性值为Controller)还是当前的Action方法上(Scope属性值为Action)。

四、Controller与ControllerInstanceFilterProvider

提到ASP.NET MVC的筛选器,大部分的都只会想到通过FilterAttribute特性,实际上Controller本身(继承自抽象类Controller)就是一个筛选器。如下面的代码片断所示,抽象类Controller实现了IActionFilter、IAuthorizationFilter、IExceptionFilter和IResultFilter这四个对应着不同筛选器类型的接口。

public abstract class Controller : ControllerBase,
  IActionFilter,
  IAuthorizationFilter,
  IExceptionFilter,
  IResultFilter,
   ...
{
  //省略成员
}
로그인 후 복사


针对Controller对象这种独特筛选器的FilterProvider类型为具有如下定义的ControllerInstanceFilterProvider。在实现的GetFilters方法中,它会根据指定的Controller上下文获取对应的Controller对象,并以此创建一个Filter(Controller对象作为Filter对象的Instance属性值)。该Filter的Scope不是Controller,而是First,而Order的值为-2147483648(Int32.MinValue),毫无疑问这样的Filter肯定第一个被执行。

public class ControllerInstanceFilterProvider : IFilterProvider
{ 
  public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); 
}
로그인 후 복사


五、GlobalFilterCollection

通过FilterAttribute的形式定义的筛选器需要显式地标注到目标Controller类型或者Action方法上,而在有些情况下需要一种全局的Filter。所谓全局筛选器,就是不需要显式与某个Controller或者Action进行匹配,而是默认使用到所有的Action执行过程中。用于提供这种全局Filter的FilterProvider对应的类型为具有如下定义的GlobalFilterCollection。

public sealed class GlobalFilterCollection : IEnumerable<Filter>, IEnumerable, IFilterProvider
{
  public GlobalFilterCollection();
  public void Add(object filter);
  public void Add(object filter, int order);
  private void AddInternal(object filter, int? order);
  public void Clear();
  public bool Contains(object filter);
  public IEnumerator<Filter> GetEnumerator();
  public void Remove(object filter);
  IEnumerator IEnumerable.GetEnumerator();
  IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
  public int Count { get; }
}
로그인 후 복사


通过命名以及上面给出的定义可以看出GlobalFilterCollection就是一个Filter的列表而已,实现的GetFilters方法返回的就是它自己。通过GlobalFilterCollection提供的方法我们可以实现对全局Filter的添加、删除和清除操作。用于添加Filter的Add方法的参数filter不是一个Filter对象,而是一个具体筛选器(实现了相应的筛选器接口),添加的Filter对象根据该筛选器对象创建,其Scope属性被设置成Global。我们通过在Add方法指定添加Filter对象的Order属性,如果没有显示指定Order并且指定的筛选器是一个FilterAttribute特性,那么该特性的Order将会作为Filter对象的Order;否则使用-1作为Order属性值。

针对整个Web应用的全局Filter(或者说全局FilterProvider)的注册和获取可以通过静态类型GlobalFilters来实现。如下面的代码片断所示,GlobalFilters具有一个静态只读属性Filters返回一个GlobalFilterCollection对象。

public static class GlobalFilters
{ 
  public static GlobalFilterCollection Filters { get; }
}
로그인 후 복사


到目前为止,我们已经介绍了ASP.NET MVC默认提供的三种FilterProvider,以及各自采用得Filter提供机制。当用于注册FilterProvider的静态类型在加载的时候,会默认创建这三种类型的对象并将其作为表示全局FilterProvider集合的Providers属性值,具体的逻辑体现在如下的代码片断中。也就是说,在默认的情况下ASP.NET MVC会采用这三种FilterProvider来提供所有的Filter对象。

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;}
}
로그인 후 복사


六、实例演示:验证Filter的提供机制和执行顺序

为了让读者对上面介绍的Filter提供机制具有一个更加深刻的映像,我们来做一个简单的实例演示。在一个通过Visual Studio的ASP.NET MVC项目模板创建的空Web项目中,我们定义了如下一个几个FilterAttribute。FilterBaseAttribute是一个实现了IActionFilter接口的抽象类型,三个具体的FilterAttribute(FooAttribute、BarAttribute和BazAttribute)是它的继承者。

public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter
{
  public void OnActionExecuted(ActionExecutedContext filterContext)
  {}
  public void OnActionExecuting(ActionExecutingContext filterContext)
  {}
}
public class FooAttribute : FilterBaseAttribute
{}
public class BarAttribute : FilterBaseAttribute
{}
public class BazAttribute : FilterBaseAttribute
{}
로그인 후 복사


我们首先在Global.asax中通过如下的方式将BazAttribute注册为一个全局筛选器。需要注意的是定义在默认创建的Global.asax中的Application_Start方法会调用RegisterGlobalFilters方法注册一个类型为HandleErrorAttribute的ExceptionFilter,我们需要将这行代码注释。

public class MvcApplication : System.Web.HttpApplication
{
  //其他成员
  protected void Application_Start()
  {   
    //其他操作
    //RegisterGlobalFilters(GlobalFilters.Filters);   
    GlobalFilters.Filters.Add(new BazAttribute());
  }
}
로그인 후 복사


最后我们创建如下一个默认的HomeController,一个空的Action方法Data上应用了我们定义的BarAttribute特性,而HomeController类上则应用了FooAttribute特性。在默认的Action方法Index中,我们通过FilterProviders的静态属性Providers表示的全局FilterProvider列表得到针对于Action方法Data的所有Filter对象,并将它们的基本信息(类型、Order和Scope属性)呈现出来。

[Foo]
public class HomeController : Controller
{
  public void Index()
  {
    ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController));
    ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "Data");
    foreach (var filter in FilterProviders.Providers.GetFilters(ControllerContext, actionDescriptor))
    {
      Response.Write(string.Format("{0}<br/>",filter.Instance));
      Response.Write(string.Format("    {0}: {1}<br/>", "Order",filter.Order));
      Response.Write(string.Format("    {0}: {1}<br/><br/>", "Scope",filter.Scope));
    }
  }
  [Bar]
  public void Data()
  { }
}
로그인 후 복사


运行我们的程序之后会在浏览器中呈现如图7-5所示的结果。我们可以清楚地看到,不仅仅应用在自身Action方法的FilterAttribute会应用到目标Action上,应用在Controller类的FilterAttribute、全局注册的Filter以及Controller对象本身体现的Filter都回最终应用在所有的Action上面。


上图将应用于Action方法Data的4个Filter的Order和Scope属性显示出来。我们在前面提到这两个属性决定了同类筛选器执行的顺序,我们现在利用这个程序要证实这一点。为此我们需要对FilterBaseAttribute作如下的修改,在OnActionExecuting中我们将当前执行的FilterAttribute的类型的方法名呈现出来。

public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter
{
  public void OnActionExecuted(ActionExecutedContext filterContext)
  {}
  public void OnActionExecuting(ActionExecutingContext filterContext)
  {
    filterContext.HttpContext.Response.Write(string.Format("{0}.OnActionExecuting()<br/>", this.GetType()));
  }
}
로그인 후 복사


然后我们按照相同的方式重写了HomeController的OnActionExecuting方法,将HomeController自身的类型的当前方法名称呈现出来。

[Foo]
public class HomeController : Controller
{
  //其他成员
  protected override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    Response.Write("HomeController.OnActionExecuting()<br/>");
  }
  [Bar]
  public void Data()
  { }
}
로그인 후 복사


我们再次运行我们的程序,并在浏览器上指定正确的地址访问定义在HomeController的Action方法Data,会在浏览器中呈现如下图所示的结果。输出的结果体现了应用到Action方法Data上的四个ActionFilter执行的顺序,而这是和Filter对应的Order和Scope属性值是一致的。


关于Filter的提供还另一个值得深究的问题:我们在定义FilterAttribute的时候可以将应用在该类型上的AttributeUsageAttribute的AllowMultiple属性设置为False使它只能在同一个目标元素上应用一次。但是,我们依然可以在Action方法和所在的Controller类型上应用它们,甚至可以将它们注册为全局Filter,那么这些FilterAttribute都将有效吗?

我们现在就来通过实例来验证这一点。现在我们删除所有的FilterAttribute,定义如下一个类型为FooAttribute的ActionFilter,我们将应用在它上面的AttributeUsageAttribute特性的AllowMultiple属性设置为False。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class FooAttribute : FilterAttribute, IActionFilter
{
  public void OnActionExecuted(ActionExecutedContext filterContext)
  { }
  public void OnActionExecuting(ActionExecutingContext filterContext)
  { }
}
로그인 후 복사


现在我们将该FooAttribute特性同时应用在HomeController类型和Action方法Data上,然后在Global.asax中注册一个针对FooAttribute特性的全局Filter。

[Foo]
public class HomeController : Controller
{
  //其他成员
  [Foo]
  public void Data()
  { }
}
public class MvcApplication : System.Web.HttpApplication
{
  //其他成员
  protected void Application_Start()
  {
    //其他操作
    //RegisterGlobalFilters(GlobalFilters.Filters);
    GlobalFilters.Filters.Add(new FooAttribute());
  }
}
로그인 후 복사

   


现在我们直接运行我们的程序,开启的浏览器中会呈现出如图7-7所示的结果。可以清楚地看到虽然我们 在三个地方注册了FooAttribute,但是由于该特性的AllowMultiple属性为False,所以只有其中一个FooAttribute最终是有效的。

ASP.NET MVC的筛选器

AllowMultiple 속성이 False인 FilterAttribute에 대해 서로 다른 범위로 여러 속성을 등록하면 결국 어떤 것이 유효할까요? 위 그림에서 볼 수 있듯이 Action 메소드(Scope는 Action)에 적용된 FooAttribute가 유효합니다. 실제로 구체적인 논리는 다음과 같습니다. 생성된 모든 필터는 Order+Scope(즉, 필터가 실행되는 순서)에 따라 정렬되고 마지막 순위의 필터가 선택됩니다. 이 예에서 제공된 세 개의 필터는 동일한 Order 속성 값(-1)을 가지며 결국 모두 범위(범위, 컨트롤러 및 작업)에 따라 정렬됩니다. 마지막 필터는 당연히 범위가 Action 입니다.

이상은 이 글의 전체 내용입니다. 모든 분들의 공부나 업무에 조금이나마 도움이 되었으면 좋겠습니다. 궁금한 점이 있으시면 메시지를 남겨주세요. PHP 중국어 웹사이트를 지원하길 바랍니다!


ASP.NET MVC 필터에 대한 더 자세한 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!


관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿