一、ActionFilter入门
使用场景
一般用于Action的日志记录。
官方介绍
实操
1、创建CustomActionFilterAttribute
类,需实现Attribute
(实现了Attribute才可在控制器中进行标注)和IActionFilter
接口。
2、CustomActionFilterAttribute
类代码如下:
/// <summary>
/// 自定义ActionFilter
/// </summary>
public class CustomActionFilterAttribute :Attribute,IActionFilter
{
/// <summary>
/// action执行后
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("正在执行OnActionExecuted。。。");
}
/// <summary>
/// action执行前
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
string controllerName = context.ActionDescriptor.RouteValues["controller"];
string actionName = context.ActionDescriptor.RouteValues["action"];
string res = $"Controller:{controllerName},Action:{actionName}\n{context.Result}";
Console.WriteLine("正在执行OnActionExecuting。。。");
}
}
3、打开HomeController
,在构造函数和Index函数中填写以下代码:
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
Console.WriteLine("正在执行HomeController构造函数。。。");
}
[CustomActionFilter]//标记自定义ActionFilter
public IActionResult Index()
{
Console.WriteLine("正在执行HomeController的Index函数。。。");
return View();
}
4、运行项目,可看到控制台进行如下输出,说明当前的执行顺序为:
“构造函数”->“OnActionExecuting”->“Index函数”->“OnActionExecuted”。
二、Filter传参
当想要在Filter使用日志中间件时,会提示需要传递形参"logger"(原因是自定义的Filter没法直接支持依赖注入),此时提供一个无参构造函数可以通过编译,但是运行时可以发现_logger参数就为空(因为都没走有参构造函数)。那么该如何解决呢?微软给我们提供了"TypeFilter"和"ServiceFilter"来支持依赖注入。
TypeFilter
此时将控制器中的代码做如下更改,可看到不会报错,且运行项目后可看到正确的输出(注:将 Console.WriteLine()更改为_logger.LogInformation(),具体请看文末的源码链接)。
//[CustomActionFilter] //是一个特性,故可以去掉后面的Attribute,无参
[TypeFilter(typeof(CustomActionFilterAttribute))]//此时不是特性,故需要完整类名
public IActionResult Index()
{
Console.WriteLine("正在执行HomeController的Index函数。。。");
return View();
}
ServiceFilter
1、此时将控制器中Index函数的特性更改为[ServiceFilter(typeof(CustomActionFilterAttribute))]
,并运行项目,会出现如下的错误:
InvalidOperationException: No service for type 'FilterWebApp.Utils.Filters.CustomActionFilterAttribute' has been registered.
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
2、上述错误告诉我们需要注册服务,此时我们在startup.cs
中对该类进行注册后,重新运行,就不会报错了。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<CustomActionFilterAttribute>();//单例注册服务
}
3、在这里看一下官方解析(官方的建议是使用服务时才使用ServiceFilter):
ServiceFilter主要用于FilterCollection.AddService调用。与Microsoft.AspNetCore.Mvc.TypeFilterAttribute类似,两者都使用构造函数注入。如果筛选器本身不是服务,请改用Microsoft.AspNetCore.Mvc.TypeFilterAttribute。
三、Filter作用域
1、添加CustomControllerActionFilterAttribute
和CustomGlobalActionFilterAttribute
,一个用于标注控制器,一个用于全局注册,与之前标注在函数上的进行比较,打印输出他们的执行顺序,并进行分析。
2、三个Filter的代码如下:
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FilterWebApp.Utils.Filters
{
/// <summary>
/// 自定义ActionFilter
/// </summary>
public class CustomActionFilterAttribute :Attribute,IActionFilter
{
//private readonly ILogger<CustomActionFilterAttribute> _logger;
//public CustomActionFilterAttribute(ILogger<CustomActionFilterAttribute> logger)
//{
// _logger = logger;
// _logger.LogInformation("正在执行CustomActionFilterAttribute构造函数");
//}
public CustomActionFilterAttribute()
{
Console.WriteLine("正在执行Action Filter 的构造函数。。。");
}
/// <summary>
/// action执行后
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("正在执行Action Filter 的OnActionExecuted。。。");
//_logger.LogInformation("Log:正在执行OnActionExecuted。。。");
}
/// <summary>
/// action执行前
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
string controllerName = context.ActionDescriptor.RouteValues["controller"];
string actionName = context.ActionDescriptor.RouteValues["action"];
string res = $"Controller:{controllerName},Action:{actionName}\n{context.Result}";
Console.WriteLine("正在执行Action Filter 的OnActionExecuting。。。");
// _logger.LogInformation("Log:正在执行OnActionExecuting。。。");
}
}
/// <summary>
/// 自定义控制器ActionFilter
/// </summary>
public class CustomControllerActionFilterAttribute : Attribute, IActionFilter
{
public CustomControllerActionFilterAttribute()
{
Console.WriteLine("正在执行Controller Filter的构造函数");
}
/// <summary>
/// action执行后
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("正在执行Controller Filter的OnActionExecuted。。。");
}
/// <summary>
/// action执行前
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("正在执行Controller Filter的OnActionExecuting。。。");
}
}
/// <summary>
/// 全局ActionFilter
/// </summary>
public class CustomGlobalActionFilterAttribute : Attribute, IActionFilter
{
public CustomGlobalActionFilterAttribute()
{
Console.WriteLine("正在执行Global Filter 的构造函数。。");
}
/// <summary>
/// action执行后
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("正在执行Global Filter 的OnActionExecuted。。。");
}
/// <summary>
/// action执行前
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("正在执行Global Filter 的OnActionExecuting。。。");
}
}
}
3、执行顺序为"Global Executing"->“Controller Executing”->“Action Executing”->“Action Executed”->“Controller Executed”->“Global Executed”,如下图:
官方提供的管道中间件执行流程图:
项目输出:
注:如果想更改Filter的执行顺序,可通过实现IOrderedFilter接口,也可以直接继承官方的ActionFilterAttribute
,这个是官方对IActionFilter
、IOderedFilter
等接口的实现,如下:
四、源码
https://gitee.com/wusuoweixgy/NET5Code/tree/master/FilterWebApp