ASP.NET Core MVC 之过滤器(Filter)

  ASP.NET MVC 中的过滤器容许在执行管道中的特定阶段以前或以后运行代码。能够对全局,也能够对每一个控制器或每一个操做配置过滤器。算法

1.过滤器如何工做缓存

  不一样的过滤器类型在管道中的不一样阶段执行,所以具备各自的与其场景。根据须要执行的任务以及须要执行的请求管道中的位置,选择要建立的过滤器类型。过滤器在 MVC 操做调用管道中运行,有时也称为过滤管道,在 MVC 中选择要执行的操做后,执行操做上的过滤器,如图:框架

  不一样的过滤器在管道内的不一样位置执行。像受权过滤器这样的过滤器只在管道中靠前的位置执行。其余过滤器,如操做(Action)过滤器,能够在管道执行的其余部分以前和以后执行,如图:异步

  

  1.选择过滤器async

  受权过滤器用于肯定当前请求用户是否被受权。ide

  资源过滤器是在受权以后第一个处理请求的过滤器,也是最后一个在请求离开过滤管道时接触请求的过滤器。在性能方面,对实现缓存或者对过滤管道进行短路 特别有用。函数

  操做过滤器包装对单个操做方法的调用,而且能够处理传递到操做的参数以及从操做返回的操做结果。性能

  异常过滤器用于对 MVC 应用程序中未处理的异常应用全局策略。ui

  结果过滤器包装单个操做结果的执行,而且尽在操做执行成功时运行。它们必须是围绕视图执行或格式化程序执行的逻辑的理想选择。spa

  

  2.实现过滤器

  全部过滤器都可经过不一样的接口定义支持同步和异步的实现。根据须要执行的任务类型,选择同步或异步实现。从框架的角度看,它们是能够互换的。

  同步过滤器定义了 OnStageExecuting 和 OnStageExecuted 方法(也有例外)。OnStageExecuting 方法在事件管道阶段以前经过阶段名称来调用,而 OnStageExecuted 方法将在阶段名称命名的管道阶段以后调用。

    public class SampleActionFilter:IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            //操做执行前作的事情
        }
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //操做执行后作的事情
        }
    }

  异步过滤器定义了一个单一的 OnActionExecutionAsync  方法,能够在具体管道阶段的先后运行。 OnActionExecutionAsync 方法提供了一个 ActionExecutionDelegate 委托,调用时该委托会执行具体管道阶段的工做,而后等待完成。

public class SampleAsyncActionFilter: IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //操做执行前作的事情
            await next();
            //操做执行后作的事情
        }
    }

  

  3.过滤器做用域

  过滤器有三种不一样级别的做用域。你能够在特定的操做上用特性(Attribute)的方式使用特定的过滤器。也能够在控制器上用特性的方式使用过滤器,这样就能够将效果做用在控制器内的全部操做上。或者注册一个全局过滤器,它将做用于整个 MVC 应用程序的每个操做。

  若是想要使用全局过滤器,能够在配置 MVC 时,在 Startup 的 ConfigureServices 方法中添加:

services.AddMvc(options =>
            {
                options.Filters.Add(typeof(SampleActionFilter));//经过类型
                options.Filters.Add(new SampleActionFilter());//注册实例
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

  过滤器既能够经过类型添加,也能够经过实例添加。若是经过实例添加,则该实例会被使用于每个请求。若是经过类型添加,则在每次请求后都会建立一个实例,其全部构造函数依赖项都将经过 DI 来填充。

  把过滤器接口的实现看成特性使用也很是方便。过滤器特性可应用于控制器和操做方法。框架包含了内置的基于特性的过滤器,能够继承他们或者另外定制。例如,下面的过滤器继承了 ResultFilterAttribute,并重写 OnResultExecuting 方法(在响应中增长一个信息头):

  

public class AddHeaderAttribute: ResultFilterAttribute
    {
        private readonly string _name;
        private readonly string _value;

        public AddHeaderAttribute(string name, string value)
        {
            _name = name;
            _value = value;
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                _name,new string[] { _value });
            base.OnResultExecuting(context);
        }
    }

  特性容许过滤器接受参数,以下,可将此特性添加到控制器或操做中,并为其指定所需 HTTP 头的名称和值:

[AddHeader("Author", "Ruby Lu")]
 public class HomeController : Controller
{
}

  如下几种过滤器接口能够自定义为相应特性的实现:

    ActionFilterAttribute

    ExceptionFilterAttribute

    ResultFilterAttribute

    FormatFilterAttribute

    ServiceFilterAttribute

    TypeFilterAttribute

 

  4.取消和短路

  经过设置传入过滤器方法的上下文参数中的 Result 属性,能够在过滤器管道的任意一点短路管道。好比,下面的 ShortCircuitingResourceFilter 将阻止它以后管道内的全部过滤器,包括全部操做过滤器:

public class ShortCircuitingResourceFilter:Attribute,IResourceFilter
    {
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.Result = new ContentResult() {
                Content = "短路"
            };
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {

        }
    }

 

2.配置过滤器

  全局过滤器在 Startup 中配置。基于特性的过滤器若是不须要任何依赖,能够简单地继承一个已存在地过滤器相对应地特性类型。若是要建立一个非全局做用域,但须要从依赖注入中得到依赖项的过滤器,那么在它们上面加上 ServiceFilterAttribute 或 TypeFilterAttribute 特性,这样就可用于控制器或操做了。

  1.依赖注入

  以特性形式实现的,直接添加到控制器或操做的过滤器,其构造函数不得由依赖注入提供依赖项。其缘由在于,特性所需的构造函数参数必须由使用处直接提供。这是特性原型机理的限制。

  若是过滤器须要从 DI 中得到依赖项,那么能够用如下几种方法在类或操做方法使用:

    ServiceFilterAttribute

    TypeFilterAttribute

    IFilterFactory 实现特性

  TypeFilter 将为其依赖项从 DI 中使用服务来实例化一个实例。 ServiceFilter 则从 DI 中获取一个过滤器实例。下面演示 ServiceFilter:

  先在 ConfigureServices 中注册 AddHeaderFilterWithDI 类型:services.AddScoped<AddHeaderFilterWithDI>(); 

  而后使用:

    [ServiceFilter(typeof(AddHeaderFilterWithDI))]

    public IActionResult Index()

    {  

    }

  ServiceFilterAttribute 实现了IFilterFactory 接口,它公开了一个建立 IFilter 实例的方法。在 ServiceFilterAttribute 中,IFilterFactory 接口的 CreateInstance 方法被实现为从服务容器加载指定的类型。

  TypeFilterAttribute 很是相似 ServiceFilterAttribute (也实现 IFilterFactory 接口),但它的类型不是直接从 DI 容器中解析,相反,它使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 实例化类型。

  因为这种差别,使用 TypeFilterAttribute 引用的类型不须要在使用前向容器注册,但它们仍由容器来填充其依赖项。此外,TypeFilterAttribute 能够可选的接受该类型的构造函数参数。下面是 TypeFilterAttribute 演示:

        [TypeFilter(typeof(AddHeaderAttribute),Arguments =new object[] { "Author","Ruby" })]
        public IActionResult Index()
        {
            return View();
        }

   若是有一个简单的过滤器,不须要任何参数,但有构造函数须要经过 DI 填充依赖项,那么能够继承 TypeFilterAttribute,容许使用本身命名的特性类和方法(而不是 [TypeFilterAttribute(typeof(FilterType))])。下面的过滤器显示了如何实现此功能:

 public class SampleActionFilterAttribute:TypeFilterAttribute
    {
        public SampleActionFilterAttribute() : base(typeof(SampleActionFilterImpl))
        {
        }
        private class SampleActionFilterImpl:IActionFilter
        {
            public void OnActionExecuting(ActionExecutingContext context)
            {
                //操做执行前作的事情
            }
            public void OnActionExecuted(ActionExecutedContext context)
            {
                //操做执行后作的事情
            }
        }
    }

  该过滤器可经过使用 [SampleActionFilter] 这样的语法应用于类或方法,而没必要使用 [TypeFilter] 或 [ServiceFilter] 。

  IFilterFactory 实现 IFilter ,所以在过滤器管道中,任何位置的  IFilterFactory 实例均可看成 Filter 实例来使用。当框架准备调用过滤器时,将尝试将其转换为 IFilterFactory 。若是转换成功, 则调用 CreateInstance 方法来建立将被调用的 IFilter 实例。这是一种很是灵活的设计,由于当应用程序启动时,不须要明确地设置精确地过滤器。

  你能够在本身地特性中实现 IFilterFactory 几口,做为另外一种建立过滤器的方法:

public class AddHeadWithFactoryAttribute:Attribute, IFilterFactory
    {
        public bool IsReusable { get; }
        //实现IFilterFactory
        public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        {
            return new InternalAddHeaderFilter();
        }
    }

    public class InternalAddHeaderFilter : IResultFilter
    {
        public  void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "Header Add" });
        }
        public void OnResultExecuted(ResultExecutedContext context)
        {

        }
    }

 

  2.排序

   过滤器能够应用于操做方法或控制器(经过特性)或添加到全局过滤器集合中。做用域一般也决定了排序,最接近操做的过滤器首先运行。

  除了做用域,过滤器还能够经过实现 IOrderedFilter 来重写它们的执行顺序。此接口简单的暴露了一个 int Order 属性,而且过滤器基于该属性以数字升序执行。全部内置的过滤器,包括 TypeFilterAttribute 和 ServiceFilterAttribute ,都实现 IOrderedFilter  接口。,所以当将过滤器特性应用于类或方法时,能够指定过滤器执行顺序。默认状况下,全部内置过滤器的 Order 属性都为0,所以范围用做分隔符,而且是决定性因素(除非 Order 设置为 0)。

  每一个从 Controller 基类继承的控制器都包含 OnActionExecuting 和 OnActionExecuted 方法。这些方法为给定操做包装了过滤器,它们分别最早运行和最后运行。假设没有为任何过滤器设置 Order 舒总,那么单纯基于范围的顺序为:

  控制器的 OnActionExecuting

  全局过滤器的OnActionExecuting

  类过滤器的OnActionExecuting

  方法过滤器的OnActionExecuting

  方法过滤器的OnActionExecuted 

  类过滤器的OnActionExecuted 

  全局过滤器的OnActionExecuted 

  控制器过滤器的OnActionExecuted 

   

  要修改默认的基于范围的顺序,则应显示设置类级别或者方法级别过滤器的 Order 属性。例如,将 Order = -1 添加到方法级属性:

  [MyFilter (Name = "...",Order = -1)]

  在这种状况下,小于零的值将确保此过滤器在全局和类级过滤器以前运行:

  控制器的 OnActionExecuting

  方法过滤器的OnActionExecuting

  全局过滤器的OnActionExecuting

  类过滤器的OnActionExecuting

  类过滤器的OnActionExecuted 

  全局过滤器的OnActionExecuted 

  控制器过滤器的OnActionExecuted 

  方法过滤器的OnActionExecuted 

  Controller 类的方法老是在全部过滤器以前和以后运行。这些方法不做为IFilter实例实现。也不参与IFilter排序算法。

 

  3.对比中间件

  通常来讲,过滤器用于处理业务与应用程序的横切关注点,用法和功能很像中间件,但过滤器容许你将做用范围缩小,并将其插入到应用程序中有意义的位置,例如视图以前或模型绑定以后。过滤器是 MVC 的一部分,能够访问其上下文和构造函数。例如,中间件很难检测到请求的模型验证是否产生错误,而且作出相应的响应。

相关文章
相关标签/搜索