ASP.NET Core中的过滤器/筛选器

通过使用 ASP.NET Core MVC 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码。

注意:本主题不适用于 Razor 页面。 ASP.NET Core 2.1 及更高版本支持适用于 Razor 页面的 IPageFilter 和 IAsyncPageFilter。 有关详细信息,请参阅 Razor 页面的筛选方法

内置筛选器处理一些任务,例如:

  • 授权(防止用户访问未获授权的资源)。
  • 确保所有请求都使用 HTTPS。
  • 响应缓存(对请求管道进行短路出路,以便返回缓存的响应)。

可以创建自定义筛选器,用于处理横切关注点。过滤器可以避免在action中编写一些重复性的代码。比如异常过滤器可以合并处理异常。

筛选器的工作原理

筛选器在 MVC 操作调用管道(有时称为筛选器管道)内运行。 筛选器管道在 MVC 选择了要执行的操作(controller中的action方法)之后运行。

筛选器类型

每种筛选器类型都在筛选器管道中的不同阶段(上图中的mvc action invocation pipeline)执行。

  • 授权筛选器最先运行,用于确定是否已针对当前请求为当前用户授权。 如果请求未获授权,它们可以让管道短路。

//startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(action =>
{ action.Filters.Add<AuthorizationFilter>();
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
} //authorizationfilter:
public class AuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly ILoggerFactory loggerFactory; public AuthorizationFilter(ILoggerFactory loggerFactory)
{
this.loggerFactory = loggerFactory;
}
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var logger = loggerFactory.CreateLogger<AuthorizationFilter>();
logger.LogWarning($"authorization filter is executing now ,target action is :{context.ActionDescriptor.DisplayName}");
return Task.CompletedTask;
}
}

  • 资源筛选器是授权后最先处理请求的筛选器。 它们可以在筛选器管道的其余阶段运行之前以及管道的其余阶段完成之后运行代码。 出于性能方面的考虑,可以使用它们来实现缓存或以其他方式让筛选器管道短路。 它们在模型绑定之前运行,所以可以影响模型绑定。

//startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(action =>
{
action.Filters.Add<AuthorizationFilter>();
action.Filters.Add<ResourceFilter>();
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
//resourcefilter:
public class ResourceFilter : IAsyncResourceFilter
{
private readonly ILoggerFactory loggerFactory; public ResourceFilter(ILoggerFactory loggerFactory)
{
this.loggerFactory = loggerFactory;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
var logger = loggerFactory.CreateLogger<ResourceFilter>();
logger.LogWarning($"resource filter is executing now,valueproviderfactories count:{context.ValueProviderFactories.Count}");
var executedContext = await next();
logger.LogWarning($"resource filter is executed now ,result's type is {executedContext.Result.GetType().Name}"); }
}

  • 操作筛选器可以在调用单个操作方法之前和之后立即运行代码。 它们可用于处理传入某个操作的参数以及从该操作返回的结果。

//startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(action =>
{
action.Filters.Add<ActionsFilter>();
action.Filters.Add<AuthorizationFilter>();
action.Filters.Add<ResourceFilter>();
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
//actionsfilter
public class ActionsFilter : IAsyncActionFilter
{
private readonly ILoggerFactory factory; public ActionsFilter(ILoggerFactory factory)
{
this.factory = factory;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var logger = factory.CreateLogger<ActionsFilter>();
logger.LogWarning($"action filter is executing new ,context.modelstate:{context.ModelState.IsValid}");
var executedContext = await next();
logger.LogWarning($"action filter is executed now,executedContext controller:{executedContext.Controller.ToString()}");
}
}

  • 异常筛选器用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。

 //startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(action =>
{
action.Filters.Add<ActionsFilter>();
action.Filters.Add<AuthorizationFilter>();
action.Filters.Add<ResourceFilter>();
action.Filters.Add<ExceptionsFilter>();
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
//exceptionsfilter:
public class ExceptionsFilter : IAsyncExceptionFilter
{
private readonly ILoggerFactory loggerFactory; public ExceptionsFilter(ILoggerFactory loggerFactory)
{
this.loggerFactory = loggerFactory;
}
public Task OnExceptionAsync(ExceptionContext context)
{
var logger = loggerFactory.CreateLogger<ExceptionsFilter>();
logger.LogWarning($"some exception's happened,exception's message:{context.Exception.Message}");
context.Result=new ObjectResult(context.Exception.Message);//这个异常被处理了一下,以200正常返回。
return Task.CompletedTask;
}
}

上面的日志打印结果可以看出exception filter实在authorization filter和resource filter以及action filter之后执行的它能捕获的异常是在action执行过程中发生的异常。所以,如果在authorization filter或者resource filter中发生异常的话,它是没有办法捕获的,可以做一个测验:将authorization filter中抛出一个异常:

可以看到这个异常是被直接抛出来了,并没有被exception handler中进行处理。接着改在resource filter中抛出一个异常,看看:

同样,在resource filter中抛出的异常exception filter也是处理不了的。也就是说在筛选器管道中,处于exception筛选器执行之前而执行的代码抛出的异常,exception筛选器是处理不了的。要想捕获程序的全局异常,我觉得应该在中间件中定义对异常的捕获。这个结论还没有进行证实,有时间再讨论。

  • 结果筛选器可以在执行单个操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。

//startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(action =>
{
action.Filters.Add<ActionsFilter>();
action.Filters.Add<AuthorizationFilter>();
action.Filters.Add<ResourceFilter>();
action.Filters.Add<ExceptionsFilter>();
action.Filters.Add<ResultFilter>();
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
//resultfilter:
public class ResultFilter : IAsyncResultFilter
{
private readonly ILoggerFactory loggerFactory; public ResultFilter(ILoggerFactory loggerFactory)
{
this.loggerFactory = loggerFactory;
}
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var logger = loggerFactory.CreateLogger<ResultFilter>();
logger.LogWarning($"result filter is executing, context.result is :{context.Result.GetType().Name}");
var executedContext = await next();
logger.LogWarning($"result filter is executed ,context.result is {executedContext.Result.GetType().Name}");
}
}

上面的结果是action中没有抛出异常,正常执行的结果,如果在action中抛出异常:

上下对比一下会发现,result filter只会在action正常执行没有抛出异常之后才会执行。exception filter是会捕获action抛出的异常。

下图展示了这些筛选器类型在筛选器管道中的交互方式。

实现

通过不同的接口定义,筛选器同时支持同步和异步实现。例如我上面举的例子全部都是用异步的方式实现的。

可在其管道阶段之前和之后运行代码的同步筛选器定义 OnStageExecuting 方法和 OnStageExecuted 方法。 例如,在调用操作方法之前调用 OnActionExecuting,在操作方法返回之后调用 OnActionExecuted

using FiltersSample.Helper;
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersSample.Filters
{
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
} public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
}
}
}

异步筛选器定义单一的 OnStageExecutionAsync 方法。 此方法采用 FilterTypeExecutionDelegate 委托来执行筛选器的管道阶段。 例如,ActionExecutionDelegate 调用该操作方法(如果没有下一个action filter)或下一个操作筛选器(下一个action filter),用户可以在调用它之前和之后执行代码。

可以在单个类中为多个筛选器阶段实现接口。 例如,ActionFilterAttribute 类实现 IActionFilter 和 IResultFilter,以及它们的异步等效接口。

注意:同步和异步的只需要实现一个就行,如果两个都实现了,会优先执行异步版本的。

篇幅太长,再来一片吧。

asp.net core 2.2 中的过滤器/筛选器(上)的更多相关文章

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

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

  2. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  3. ASP.NET Core 1.0 中的依赖项管理

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  4. 在ASP.NET Core 1.0中如何发送邮件

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:目前.NET Core 1.0中并没有提供SMTP相关的类库,那么要如何从ASP.NE ...

  5. ASP.NET Core 1.0 中使用 Swagger 生成文档

    github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...

  6. 用ASP.NET Core 1.0中实现邮件发送功能

    准备将一些项目迁移到 asp.net core 先从封装类库入手,在遇到邮件发送类时发现在 asp.net core 1.0中并示提供SMTP相关类库,于是网上一搜发现了MailKit 好东西一定要试 ...

  7. 在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

    HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务 ...

  8. 在ASP.NET Core 2.0中使用CookieAuthentication

    在ASP.NET Core中关于Security有两个容易混淆的概念一个是Authentication(认证),一个是Authorization(授权).而前者是确定用户是谁的过程,后者是围绕着他们允 ...

  9. 使用Http-Repl工具测试ASP.NET Core 2.2中的Web Api项目

    今天,Visual Studio中没有内置工具来测试WEB API.使用浏览器,只能测试http GET请求.您需要使用Postman,SoapUI,Fiddler或Swagger等第三方工具来执行W ...

随机推荐

  1. 死磕 java集合之TreeSet源码分析

    问题 (1)TreeSet真的是使用TreeMap来存储元素的吗? (2)TreeSet是有序的吗? (3)TreeSet和LinkedHashSet有何不同? 简介 TreeSet底层是采用Tree ...

  2. Redis学习笔记~分布锁的使用

    回到目录 分布锁主要用在多进程共同访问同一个资源时候,用来保持同一时间段只能有一个进程执行,同时避免了并发冲突的出现,这在很多场景都会用到,像秒杀库存,抽奖库存,多操作者处理一家公司等. void T ...

  3. 基于.NetCore的Redis5.0.3(最新版)快速入门、源码解析、集群搭建与SDK使用【原创】

    1.[基础]redis能带给我们什么福利 Redis(Remote Dictionary Server)官网:https://redis.io/ Redis命令:https://redis.io/co ...

  4. java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)

    Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...

  5. DSAPI 远程协助之获取当前鼠标指针坐标及样式

    在编写远程协助类软件时,除了获取屏幕画面外,还需要获取鼠标当前的坐标以及当前的指针样式,以便让远程屏幕知道当前的操作状态. 使用DSAPI内置的鼠标指针类,可轻松获取. 代码如下: Imports D ...

  6. 大数据时代的图表可视化利器——highcharts,D3和百度的echarts

    大数据时代的图表可视化利器——highcharts,D3和百度的echarts https://blog.csdn.net/minidrupal/article/details/42153941   ...

  7. 基于IIS的WCF

    (1)创建WCF服务应用程序 (2)配置IIS 将WCF服务应用程序配置IIS网站,需要使用.net4.0集成版本的程序池 (3)使用SvcUtil.exe生成客户端代码和配置 SvcUtil.exe ...

  8. 解决vue数据渲染过程中的闪动问题

    关键代码 主要解决vue双大括号{{}}在数据渲染和加载过程中的闪动问题,而影响客服体验. html代码: <span class="tableTitle selftab" ...

  9. 47.Odoo产品分析 (五) – 定制板块(2) – 为业务自定义odoo(2)

    查看Odoo产品分析系列--目录 Odoo产品分析 (五) – 定制板块(2) – 为业务自定义odoo(1) 4 添加自定义字段 定制odoo的最普通的原因就是指定到公司的附加信息.如果您正在运行一 ...

  10. android开发中调用python代码(带参数)

    android开发主要用到的是java代码,但是当开发涉及到一些算法时,往往用python可以提高软件的运行速度,也更加便捷,这里分享自己项目调用python代码的方式,主要有以下几个步骤(个人方法, ...