7.5筛选器(过滤器)
筛选器运行开发人员在ASP.NET Core特定的位置执行我们自己的代码,比如在控制器的操作方法之前执行数据检查,或者在ActionResult执行的时候向响应报文头中加入自定义数据。
异常筛选器
系统中出现未处理异常的时候,就会自动执行异常筛选器
- 编写筛选器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters;
public class MyExceptionFilter : IAsyncExceptionFilter { private readonly ILogger<MyExceptionFilter> logger; private readonly IHostEnvironment env; public MyExceptionFilter(ILogger<MyExceptionFilter> logger, IHostEnvironment env) { this.logger = logger; this.env = env; } public Task OnExceptionAsync(ExceptionContext context) { Exception exception = context.Exception; logger.LogError(exception, "UnhandledException occured"); string message; if (env.IsDevelopment()) { message = exception.ToString(); } else { message = "程序中出现未处理异常"; } ObjectResult result = new ObjectResult(new { code = 500, message = message }); result.StatusCode = 500; context.Result = result; context.ExceptionHandled = true; return Task.CompletedTask; } }
|
- 设置全局筛选器,在Program.cs的builder.Build之前添加
1 2 3 4 5
| builder.Services.Configure<MvcOptions>(options => { options.Filters.Add<MyExceptionFilter>(); });
|
操作筛选器
控制器中操作方法执行的时候,操作筛选器就会被执行,通常可以在操作方法执行之前或者之后执行一些代码
操作筛选器要实现IAsyncActionFilter
接口,接口中定义了
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
方法
context:代表Action执行的上下文对象,从context中可以获取请求路径等信息
next:用来指向下一个操作器的委托,一个项目可以有多个操作筛选器,如果当前操作筛选器是最后一个筛选器则next就是要执行的操作方法
- 编写操作筛选器1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| using Microsoft.AspNetCore.Mvc.Filters;
public class MyActionFilter1 : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { Console.WriteLine("MyActionFilter 1:开始执行"); ActionExecutedContext r = await next(); if (r.Exception != null) { Console.WriteLine("MyActionFilter 1:执行失败"); } else { Console.WriteLine("MyActionFilter 1:执行成功"); } } }
|
- 编写操作筛选器2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| using Microsoft.AspNetCore.Mvc.Filters;
public class MyActionFilter2 : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { Console.WriteLine("MyActionFilter 2:开始执行"); ActionExecutedContext r = await next(); if (r.Exception != null) { Console.WriteLine("MyActionFilter 2:执行失败"); } else { Console.WriteLine("MyActionFilter 2:执行成功"); } } }
|
- 在Program.cs注册筛选器
1 2 3 4 5
| builder.Services.Configure<MvcOptions>(options => { options.Filters.Add<MyActionFilter1>(); options.Filters.Add<MyActionFilter2>(); });
|
- 在控制器中增加操作方法
1 2 3 4 5 6
| [HttpGet] public string GetData() { Console.WriteLine("执行GetData"); return "hello"; }
|
- 结果

案例1 自动启用事务
数据库事务保证了我们对数据的操作要么全部成功,要么全部失败,我们可以使用TransactionScope
来操作数据库事务,将使用EF Core的数据库操作放到TransactionScope
声明的范围内,则这段代码自动支撑事务。
- 我们定义数据库操作都默认使用事务,对于不使用事务的方法,使用NotTransactionAttribute来标记
1 2
| [AttributeUsage(AttributeTargets.Method)] public class NotTransactionalAttribute : Attribute{}
|
- 实现筛选器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class TransactionScopeFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { bool hasNotTransactionalAttribute = false; if (context.ActionDescriptor is ControllerActionDescriptor) { var actionDesc = (ControllerActionDescriptor)context.ActionDescriptor; hasNotTransactionalAttribute = actionDesc.MethodInfo .IsDefined(typeof(NotTransactionalAttribute)); } if (hasNotTransactionalAttribute) { await next(); return; } using var txScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); var result = await next(); if (result.Exception == null) { txScope.Complete(); } } }
|
- 注册到Program.cs中
1 2 3 4
| builder.Services.Configure<MvcOptions>(options => { options.Filters.Add<TransactionScopeFilter>(); });
|
案例2:请求限流器
如果有客户端频繁发送请求而消耗服务器资源,则可以限制1s内只允许同一个IP的一次请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Memory;
public class RateLimitFilter : IAsyncActionFilter { private readonly IMemoryCache memCache; public RateLimitFilter(IMemoryCache memCache) { this.memCache = memCache; } public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { string removeIP = context.HttpContext.Connection.RemoteIpAddress.ToString(); string cacheKey = $"LastVisitTick_{removeIP}"; long? lastTick = memCache.Get<long?>(cacheKey); if (lastTick == null || Environment.TickCount64 - lastTick > 1000) { memCache.Set(cacheKey, Environment.TickCount64,TimeSpan.FromSeconds(10)); return next(); } else { context.Result = new ContentResult { StatusCode= 429 } ; return Task.CompletedTask; } } }
|
注册筛选器和内存缓存服务
1 2 3 4 5
| builder.Services.Configure<MvcOptions>(options => { options.Filters.Add<RateLimitFilter>(); }); builder.Services.AddMemoryCache();
|