ASP.NET MVC 中的过滤器允许在执行管道中的特定阶段之前或之后运行代码。可以对全局,也可以对每个控制器或每个操作配置过滤器。

1.过滤器如何工作

  不同的过滤器类型在管道中的不同阶段执行,因此具有各自的与其场景。根据需要执行的任务以及需要执行的请求管道中的位置,选择要创建的过滤器类型。过滤器在 MVC 操作调用管道中运行,有时也称为过滤管道,在 MVC 中选择要执行的操作后,执行操作上的过滤器,如图:

  不同的过滤器在管道内的不同位置执行。像授权过滤器这样的过滤器只在管道中靠前的位置执行。其他过滤器,如操作(Action)过滤器,可以在管道执行的其他部分之前和之后执行,如图:

  

  1.选择过滤器

  授权过滤器用于确定当前请求用户是否被授权。

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

  操作过滤器包装对单个操作方法的调用,并且可以处理传递到操作的参数以及从操作返回的操作结果。

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

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

  

  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 的一部分,可以访问其上下文和构造函数。例如,中间件很难检测到请求的模型验证是否产生错误,并且做出相应的响应。

ASP.NET Core MVC 之过滤器(Filter)的更多相关文章

  1. ASP.NET core MVC动作过滤器执行顺序

    using Microsoft.AspNetCore.Mvc.Filters; using System; using System.Threading.Tasks; namespace dotnet ...

  2. ASP.NET CORE MVC 2.0 如何在Filter中使用依赖注入来读取AppSettings,及.NET Core控制台项目中读取AppSettings

    问: ASP.NET CORE MVC 如何在Filter中使用依赖注入来读取AppSettings 答: Dependency injection is possible in filters as ...

  3. ASP.NET Core Mvc中空返回值的处理方式

    原文地址:https://www.strathweb.com/2018/10/convert-null-valued-results-to-404-in-asp-net-core-mvc/ 作者: F ...

  4. ASP.NET Core MVC 过滤器介绍

    过滤器的作用是在 Action 方法执行前或执行后做一些加工处理.使用过滤器可以避免Action方法的重复代码,例如,您可以使用异常过滤器合并异常处理的代码. 过滤器如何工作? 过滤器在 MVC Ac ...

  5. asp.net core MVC 全局过滤器之ExceptionFilter异常过滤器(一)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter异常过滤器(一) asp.net cor ...

  6. asp.net core MVC 过滤器之ActionFilter过滤器(二)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter过滤器(一) asp.net core ...

  7. 解说asp.net core MVC 过滤器的执行顺序

    asp.net core MVC 过滤器会在请求管道的各个阶段触发.同一阶段又可以注册多个范围的过滤器,例如Global范围,controller范围等.以ActionFilter为例,我们来看看过滤 ...

  8. Asp.Net Core MVC框架内置过滤器

    第一部分.MVC框架内置过滤器 下图展示了Asp.Net Core MVC框架默认实现的过滤器的执行顺序: Authorization Filters:身份验证过滤器,处在整个过滤器通道的最顶层.对应 ...

  9. asp.net core MVC 过滤器之ExceptionFilter过滤器(一)

    简介 异常过滤器,顾名思义,就是当程序发生异常时所使用的过滤器.用于在系统出现未捕获异常时的处理. 实现一个自定义异常过滤器 自定义一个异常过滤器需要实现IExceptionFilter接口 publ ...

随机推荐

  1. springboot +mybatis分页插件PageHelper

    1.问题描述 JAVA界ORM的两位大佬Hibernate和Mybatis,hb自带分页(上手挺快,以前用了好几年hb,后期运维及优化快疯了),mybatis没有分页功能,需要借助第三方插件来完成,比 ...

  2. DAX 第一篇:数据模型

    DAX是一种专门用于计算数据模型的业务公式的语言,本文以Power BI的关系来学习数据模型. 一,理解数据模型 数据模型是由一组表和关系构成的结构,表和表之间由关系链接,如下图所示的产品数据模型: ...

  3. 如何在vue中监听scroll,从而实现滑动加载更多

    首先需要明确3个定义: 文档高度:整个页面的高度 可视窗口高度:你看到的浏览器可视屏幕高度 滚动条滚动高度: 滚动条下滑过的高度 当 文档高度 = 可视窗口高度 + 滚动条高度  时,滚动条正好到底. ...

  4. Java面试题汇总---基础版(附答案)

    基于我个人对面试的认知和招聘经验,在此我总结一下Java开发者的基础知识掌握要求,及应聘者面试的需要准备的内容. 首先,Java基础是每个面试官都会问到的,可能只是针对工作经验的多少,对问题追踪深度有 ...

  5. 远程调试出现DEP0600: 部署失败。无法通过新部署管道进行部署错误解决

    昨天我连接树莓派调试没问题,今天来的时候却总是出现DEP0600: 部署失败.无法通过新部署管道进行部署.错误 我怀疑是环境问题,然后发现蓝莓派上面没有远程调试监视器(MSVSMON.EXE)进程,怀 ...

  6. Mask-RCNN:教你如何制作自己的数据集进行像素级的目标检测

    概述 Mask-RCNN,是一个处于像素级别的目标检测手段.目标检测的发展主要历程大概是:RCNN,Fast-RCNN,Fster-RCNN,Darknet,YOLO,YOLOv2,YOLO3(参考目 ...

  7. docker学习笔记-简介

    零.什么是Docker 是一个基于GO语言开发的开源 应用容器: 开发者可以打包应用和相关包,到一个 轻量级 . 可移植 的 容器 中,并且可以发布到 任何机器 ,实现 虚拟化: 完全使用 沙箱机制, ...

  8. Django REST Framework(DRF)_第一篇

    认识RESTful REST是设计风格而不是标准,简单来讲REST规定url是用来唯一定位资源,而http请求方式则用来区分用户行为. REST接口设计规范 HTTP常用动词 GET /books:列 ...

  9. 微信小程序踩坑日记1——调用微信授权窗口

    0. 引言 微信小程序为了优化用户体验,取消了在进入小程序时立马出现授权窗口.需要用户主动点击按钮,触发授权窗口. 那么,在我实践过程中,出现了以下问题. . 无法弹出授权窗口 . 希望在用户已经授权 ...

  10. Python小故事--------Tkinter的组件描述及解析

    概念 Tkinter: 是Tk图形用户界面工具包标准(ctl)的Python接口,作为一个轻量级的跨平台图形用户界面(GUI)开发工具 frame: 屏幕上的一块矩形区域,多是用来作为容器(conta ...