ASP.NET Core MVC 之过滤器(Filter)
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)的更多相关文章
- ASP.NET core MVC动作过滤器执行顺序
using Microsoft.AspNetCore.Mvc.Filters; using System; using System.Threading.Tasks; namespace dotnet ...
- ASP.NET CORE MVC 2.0 如何在Filter中使用依赖注入来读取AppSettings,及.NET Core控制台项目中读取AppSettings
问: ASP.NET CORE MVC 如何在Filter中使用依赖注入来读取AppSettings 答: Dependency injection is possible in filters as ...
- ASP.NET Core Mvc中空返回值的处理方式
原文地址:https://www.strathweb.com/2018/10/convert-null-valued-results-to-404-in-asp-net-core-mvc/ 作者: F ...
- ASP.NET Core MVC 过滤器介绍
过滤器的作用是在 Action 方法执行前或执行后做一些加工处理.使用过滤器可以避免Action方法的重复代码,例如,您可以使用异常过滤器合并异常处理的代码. 过滤器如何工作? 过滤器在 MVC Ac ...
- asp.net core MVC 全局过滤器之ExceptionFilter异常过滤器(一)
本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter异常过滤器(一) asp.net cor ...
- asp.net core MVC 过滤器之ActionFilter过滤器(二)
本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter过滤器(一) asp.net core ...
- 解说asp.net core MVC 过滤器的执行顺序
asp.net core MVC 过滤器会在请求管道的各个阶段触发.同一阶段又可以注册多个范围的过滤器,例如Global范围,controller范围等.以ActionFilter为例,我们来看看过滤 ...
- Asp.Net Core MVC框架内置过滤器
第一部分.MVC框架内置过滤器 下图展示了Asp.Net Core MVC框架默认实现的过滤器的执行顺序: Authorization Filters:身份验证过滤器,处在整个过滤器通道的最顶层.对应 ...
- asp.net core MVC 过滤器之ExceptionFilter过滤器(一)
简介 异常过滤器,顾名思义,就是当程序发生异常时所使用的过滤器.用于在系统出现未捕获异常时的处理. 实现一个自定义异常过滤器 自定义一个异常过滤器需要实现IExceptionFilter接口 publ ...
随机推荐
- Centos7 防护墙 设置端口
Centos7中的防火墙调整为firewalld,试一下systemctl stop firewalld关闭防火墙. 命令:systemctl stop firewalld 命令:systemctl ...
- Git使用小技巧之免密登录
想要获取更多文章可以访问我的博客 - 代码无止境. 小代同学在使用Git的过程中发现,每次向远程仓库推送代码的时候都需要输入账号密码.做为一个程序员,多多少少都会有偷懒的思维.那么如何才能避免每次都要 ...
- [1045] PDOException in Connection.php line 295
tp5实现登录功能时报错 在使用tp5框架实现登录功能的时候,点击login出现了 [1045] PDOException in Connection.php line 295 这个问题, 报错是 S ...
- EnjoyingSoft之Mule ESB开发教程第一篇:初识Mule ESB
目录 1. Mule ESB基本介绍 2. Mule ESB社区版和企业版 3. Mule ESB常用场景 4. Mule ESB软件安装 客户端安装 服务端安装 5. 第一个Mule ESB应用- ...
- CF1194D 1-2-K Game (博弈论)
CF1194D 1-2-K Game 一道简单的博弈论题 首先让我们考虑没有k的情况: 1. (n mod 3 =0) 因为n可以被分解成若干个3相加 而每个3可以被分解为1+2或2+1 所以无论A出 ...
- Excel催化剂图表系列之品味IBCS瀑布图观察企业利润构成
IBCS图表,每个细节都值得反复琢磨参悟,此篇给大家送上详尽的瀑布图方式下的利润数据观察.请不要拿Excel2016版提供的瀑布图与IBCS版的瀑布图作对比,那完全不是一个级别的,可以类比为拿一辆经济 ...
- python字符编码-文件操作
字符编码 字符编码历史及发展 为什么有字符编码 ''' 原因:人们想要将数据存入计算机 计算机的能存储的信息都是二进制的数据 内存是基于电工作的,而电信号只有高低频两种,就用01来表示高低电频,所以计 ...
- echarts在react项目中的使用
数据可视化在前端开发中经常会遇到,万恶的图表,有时候总是就差一点,可是怎么也搞不定. 别慌,咱们一起来研究. 引入我就不多说了 npm install echarts 对于基础的可视化组件,我一般采用 ...
- 解读equals()和hashCode()
前面部分摘自:https://blog.csdn.net/javazejian/article/details/51348320 一:Object中equals方法的实现原理 public boole ...
- docker学习ppt
保存下学习资料