官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0
通过使用 ASP.NET Core 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码。
内置筛选器处理任务,例如:
- 授权(防止用户访问未获授权的资源)。
- 响应缓存(对请求管道进行短路出路,以便返回缓存的响应)。
可以创建自定义筛选器,用于处理横切关注点。 横切关注点的示例包括错误处理、缓存、配置、授权和日志记录。 筛选器可以避免复制代码。 例如,错误处理异常筛选器可以合并错误处理。
筛选器的工作原理
筛选器在 ASP.NET Core 操作调用管道(有时称为筛选器管道)内运行。 筛选器管道在 ASP.NET Core 选择了要执行的操作之后运行。
筛选器类型
每种筛选器类型都在筛选器管道中的不同阶段执行:
-
授权筛选器(AuthorizationFilter):最先运行,用于确定是否已针对请求为用户授权。 如果请求未获授权,授权筛选器可以让管道短路。
-
资源筛选器(ResourceFilter):
- 授权后运行。
- OnResourceExecuting在筛选器管道的其余阶段之前运行代码。 例如,
OnResourceExecuting
在模型绑定之前运行代码。 - OnResourceExecuted 在管道的其余阶段完成之后运行代码。
-
操作筛选器(ActionFilter):
- 在调用操作方法之前和之后立即运行代码。
- 可以更改传递到操作中的参数。
- 可以更改从操作返回的结果。
- 不可在 Pages 中使用。
-
异常筛选器(ExceptionFilter):在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。
-
结果筛选器(ResultFilter):在执行操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。
筛选器实现
筛选器通过不同的接口定义支持同步和异步实现。同步筛选器在其管道阶段之前和之后运行代码。 例如,OnActionExecuting 在调用操作方法之前调用。 OnActionExecuted 在操作方法返回之后调用。
筛选器接口的同步和异步版本任意实现一个,而不是同时实现 。 运行时会先查看筛选器是否实现了异步接口,如果是,则调用该接口,调用之后就不会执行同步代码。
操作筛选器(ActionFilter)
继承 ActionFilterAttribute 就可以了,可以看到 ActionFilterAttribute 继承了 IResultFilter , 所以实现了结果筛选器的一些方法,如果只想实现操作筛选器,可以直接继承 Attribute, IActionFilter / IAsyncActionFilter
代码:
1 public class FcbActionFilterAttribute : ActionFilterAttribute 2 { 3 private string _msg { get; set; } 4 public FcbActionFilterAttribute(string msg ) 5 { 6 _msg = msg; 7 } 8 public override void OnActionExecuting(ActionExecutingContext context) 9 { 10 Console.WriteLine("方法过滤之前"); 11 } 12 public override void OnActionExecuted(ActionExecutedContext context) 13 { 14 Console.WriteLine("方法过滤之后"); 15 } 16 public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 17 { 18 IConfiguration _configuration = context.HttpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration; 19 context.HttpContext.Request.Headers.Add(_configuration.GetSection("admin").Value, _msg); 20 Console.WriteLine("异步方法过滤之前"); 21 var resultContext = await next(); 22 Console.WriteLine("异步方法过滤之后"); 23 } 24 public override void OnResultExecuting(ResultExecutingContext context) 25 { 26 Console.WriteLine("结果过滤之前"); 27 } 28 public override void OnResultExecuted(ResultExecutedContext context) 29 { 30 Console.WriteLine("结果过滤之后"); 31 } 32 public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) 33 { 34 Console.WriteLine("异步结果过滤之前"); 35 var resultContext = await next(); 36 Console.WriteLine("异步结果过滤之后"); 37 } 38 39 }
使用过滤器:
1 [FcbActionFilter("hello")] 2 [HttpGet("api/{controller}/{action}/{id?}")] 3 public IActionResult test() 4 { 5 Console.WriteLine("执行方法体"); 6 return Ok(HttpContext.Request.Headers.FirstOrDefault(x=>x.Key== _configuration.GetSection("admin").Value).Value); 7 }
执行结果:
1 异步方法过滤之前 2 执行方法体 3 异步方法过滤之后 4 异步结果过滤之前 5 异步结果过滤之后
执行顺序:OnActionExecuting-->OnActionExecuted --> Controller(构造函数 --> 方法) --> OnResultExecuting-->OnResultExecuted
IActionFilter能干什么?
1.登录(授权) :不推荐,应该放在最外层做控制,最好在IAuthorizationFilter中实现;
2.日志 :非常推荐,跟执行顺序有关,效果非常好;
3.缓存 :可以做,但是不推荐,最好在IResourceFilter中实现,如上图;
这里可以看到先执行操作筛选器,再执行方法体,最后执行结果筛选器
结果筛选器(ResultFilter)
继承 ResultFilterAttribute 就可以了,当然也可以继承上面操作筛选器的 ActionFilterAttribute
代码:
1 [FcbResultFilter] 2 [HttpGet("api/{controller}/{action}/{id?}")] 3 public IActionResult test() 4 { 5 Console.WriteLine("执行方法体"); 6 return Ok(HttpContext.Request.Headers.FirstOrDefault(x=>x.Key== _configuration.GetSection("admin").Value).Value); 7 }
1 public class FcbResultFilterAttribute : ResultFilterAttribute 2 { 3 //由于有异步不执行 4 public override void OnResultExecuting(ResultExecutingContext context) 5 { 6 Console.WriteLine("结果过滤之前"); 7 } 8 //由于有异步不执行 9 public override void OnResultExecuted(ResultExecutedContext context) 10 { 11 Console.WriteLine("结果过滤之后"); 12 } 13 public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) 14 { 15 Console.WriteLine("异步结果过滤之前"); 16 var resultContext = await next(); 17 Console.WriteLine("异步结果过滤之后"); 18 } 19 }
执行结果:
1 执行方法体 2 异步结果过滤之前 3 异步结果过滤之后
资源筛选器(ResourceFilter)
继承 IResourceFilter / IAsyncResourceFilter
代码:
1 [FcbActionFilter("hello")] 2 [FcbResourceFilter] 3 [HttpGet("api/{controller}/{action}/{id?}")] 4 public IActionResult test() 5 { 6 Console.WriteLine("执行方法体"); 7 return Ok(); 8 }
1 public class FcbResourceFilterAttribute : Attribute, IResourceFilter 2 { 3 public void OnResourceExecuting(ResourceExecutingContext context) 4 { 5 Console.WriteLine("资源过滤之前"); 6 } 7 public void OnResourceExecuted(ResourceExecutedContext context) 8 { 9 Console.WriteLine("资源过滤之后"); 10 } 11 }
1 public class FcbActionFilterAttribute : ActionFilterAttribute 2 { 3 private string _msg { get; set; } 4 public FcbActionFilterAttribute(string msg ) 5 { 6 _msg = msg; 7 } 8 public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 9 { 10 Console.WriteLine("异步方法过滤之前"); 11 var resultContext = await next(); 12 Console.WriteLine("异步方法过滤之后"); 13 } 14 public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) 15 { 16 Console.WriteLine("异步结果过滤之前"); 17 var resultContext = await next(); 18 Console.WriteLine("异步结果过滤之后"); 19 } 20 21 }
运行结果:
1 资源过滤之前 2 异步方法过滤之前 3 执行方法体 4 异步方法过滤之后 5 异步结果过滤之前 6 异步结果过滤之后 7 资源过滤之后
执行顺序:OnResourceExecuting --> Controller(构造函数 --> 方法) --> OnResourceExecuted
IResourceFilter能干什么?
1.缓存(全局资源) :ResourceFilter就是为了缓存而生的;
2.权限 :可以做,但是不推荐做这个,因为权限一般放在AuthorizeFilter中;
3.日志 :有更适合的Filter,IActionFilter
授权筛选器(AuthorizationFilter)
- 是筛选器管道中运行的第一个筛选器。
- 控制对操作方法的访问。
- 具有在它之前的执行的方法,但没有之后执行的方法。
不会在授权筛选器中引发异常,在授权筛选器出现异常时请小心应对。
代码:
1 [FcbAuthorizationFilter("index")] 2 [FcbResourceFilter] 3 [HttpGet("api/{controller}/{action}/{id?}")] 4 public IActionResult test() 5 { 6 7 Console.WriteLine("执行方法体"); 8 return Ok(); 9 }
1 public class FcbAuthorizationFilterAttribute : Attribute,IAuthorizationFilter 2 { 3 public string _value; 4 public FcbAuthorizationFilterAttribute(string value) 5 { 6 _value = value; 7 } 8 public void OnAuthorization(AuthorizationFilterContext context) 9 { 10 if (_value == "index") 11 { 12 Console.WriteLine("资源授权成功"); 13 } 14 else 15 { 16 context.Result = new ContentResult() 17 { 18 StatusCode = 401, 19 Content = "没有权限" 20 }; 21 } 22 } 23 }
1 public class FcbResourceFilterAttribute : Attribute, IResourceFilter 2 { 3 public void OnResourceExecuting(ResourceExecutingContext context) 4 { 5 Console.WriteLine("资源过滤之前"); 6 } 7 public void OnResourceExecuted(ResourceExecutedContext context) 8 { 9 Console.WriteLine("资源过滤之后"); 10 } 11 }
运行结果:
1 资源授权成功 2 资源过滤之前 3 执行方法体 4 资源过滤之后
异常筛选器(ExceptionFilter)
- 没有之前和之后的事件。
- 实现 OnException 或 OnExceptionAsync。
- 处理 Razor 页面或控制器创建、Razor、操作筛选器或操作方法中发生的未经处理的异常。
- 请不要捕获资源筛选器、结果筛选器或 MVC 结果执行中发生的异常。
若要处理异常,将 属性 ExceptionHandled 设置为 或 true 分配 Result 属性。 这将停止传播异常。 异常筛选器无法将异常转变为“成功”。 只有操作筛选器才能执行该转变
代码:
1 [FcbResultFilter] 2 [HttpGet("api/{controller}/{action}/{id?}")] 3 public IActionResult test() 4 { 5 6 Console.WriteLine("执行方法体"); 7 throw new Exception("抛出异常"); 8 return Ok(); 9 }
1 public class FcbResultFilterAttribute : ResultFilterAttribute 2 { 3 public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) 4 { 5 Console.WriteLine("异步结果过滤之前"); 6 var resultContext = await next(); 7 Console.WriteLine("异步结果过滤之后"); 8 } 9 }
1 public class FcbExceptionFilterAttribute: ExceptionFilterAttribute 2 { 3 public override Task OnExceptionAsync(ExceptionContext context) 4 { 5 Console.WriteLine(context.Exception.Message); 6 context.Result = new ObjectResult(new 7 { 8 code = 500, 9 message = context.Exception.Message 10 }); 11 return Task.CompletedTask; 12 } 13 }
将异常注册在服务里面设置全局监控异常,当然也可以通过特性的方式监控某个接口或控制器
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddControllersWithViews(configure=> { 4 configure.Filters.Add(typeof(FcbExceptionFilterAttribute)); 5 }); 6 7 }
运行结果:
1 执行方法体 2 抛出异常
控制器级别筛选器
除了以上几种筛选器,这里还隐藏了一个控制里面的筛选器,继承自 Controller 基类的每个控制器都包括 Controller.OnActionExecuting 、 Controller.OnActionExecutionAsync 和 Controller.OnActionExecuted 方法。 这些方法:
- 覆盖为给定操作运行的筛选器。
OnActionExecuting
在所有操作筛选器之前调用。OnActionExecuted
在所有操作筛选器之后调用。OnActionExecutionAsync
在所有操作筛选器之前调用。next
之后的筛选器中的代码在操作方法之后运行。
代码:
1 public class HomeController : Controller 2 { 3 private readonly ILogger<HomeController> _logger; 4 private IConfiguration _configuration; 5 public HomeController(ILogger<HomeController> logger, IConfiguration configuration) 6 { 7 _logger = logger; 8 _configuration = configuration; 9 } 10 public override void OnActionExecuting(ActionExecutingContext context) 11 {<br> //通过设置Result来实现短路,就不会执行接下来的操作筛选器 12 //context.Result =Ok("异常"); 13 // Do something before the action executes. 14 Console.WriteLine("控制器筛选器执行之前"); 15 base.OnActionExecuting(context); 16 } 17 18 public override void OnActionExecuted(ActionExecutedContext context) 19 { 20 21 // Do something after the action executes. 22 Console.WriteLine("控制器筛选器执行之后"); 23 base.OnActionExecuted(context); 24 } 25 26 [HttpGet("api/{controller}/{action}/{id?}")] 27 public IActionResult test() 28 { 29 Console.WriteLine("执行方法体"); 30 return Ok(HttpContext.Request.Headers.FirstOrDefault(x=>x.Key== _configuration.GetSection("admin").Value).Value); 31 } 32 }
运行结果:
1 控制器筛选器执行之前 2 异步方法过滤之前 3 执行方法体 4 异步方法过滤之后 5 控制器筛选器执行之后 6 异步结果过滤之前 7 异步结果过滤之后
取消和设置短路
通过设置提供给筛选器方法的 ResourceExecutingContext 参数上的 Result 属性,可以使筛选器管道短路。 例如,以下资源筛选器将阻止执行管道的其余阶段:
1 public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter 2 { 3 public void OnResourceExecuting(ResourceExecutingContext context) 4 { 5 context.Result = new ContentResult() 6 { 7 Content = "Resource unavailable - header not set." 8 }; 9 } 10 11 public void OnResourceExecuted(ResourceExecutedContext context) 12 { 13 } 14 }标签:Core,Console,进阶,WriteLine,异步,C#,context,筛选,public From: https://www.cnblogs.com/2023-02-14/p/16814541.html