过滤器

过滤器与中间件很相似,能够在某些功能前后执行,由此而形成一个管道

ASP.NET Core MVC 提供了5种类型的过滤器

  • AuthorizationFilter:最先执行,用于判断用户是否授权如果未授权直接结束当前请求
  • ResourceFilter:在Authorization后执行,可以用来对请求判断是否执行Action
  • ActionFilter:在Action执行的前后执行,与Resource不同的是,它在模型绑定之后执行。
  • ExceptionFilter:用于捕获异常
  • ResultFilter:在最后执行,可以控制Action执行的结果

以上五种类型过滤器的工作顺序

graph TDA[中间件] --> B[AuthorizationFilter] B --> C[ResourceFilter] C --> D[ExceptionFilter] D --> F[模型绑定] F --> G[ActionFilter] G --> H[Action] H --> I[ActionFilter] I --> L[ResultAction]

快速入门

Action过滤器将在Controller的Action执行之前和执行相应的方法

首先我们新建一个类,继承于IActionFilter,查看IActionFilter里面会发现有两个接口

 //
    // 摘要:
    //     A filter that surrounds execution of the action.
    public interface IActionFilter : IFilterMetadata
    {
        //
        // 摘要:
        //     Called after the action executes, before the action result.
        //
        // 参数:
        //   context:
        //     The Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext.
        void OnActionExecuted(ActionExecutedContext context);
        //
        // 摘要:
        //     Called before the action executes, after model binding is complete.
        //
        // 参数:
        //   context:
        //     The Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.
        void OnActionExecuting(ActionExecutingContext context);
    }

根据描述我们可得知第一个是在执行方法之后执行,第二个在执行方法之前执行

然后就可以写我们自己的过滤器啦

    public class TestActionFilterAttribute : Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            Console.WriteLine("OnActionExecuted执行了");
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine("OnActionExecuting执行了");
        }
    }

心细的小伙伴可能看到我这里还继承了Attribute,没错可以直接通过特性应用在方法或者类上表示对这个方法或者类进行过滤

[HttpGet]
[TestActionFilter]
public IActionResult Test()
{
    Console.WriteLine("Action执行");
    return Ok(new
    {
        msg = "OK"
    });
}
[Route("api/[controller]")]
[ApiController]
[TestActionFilter]
public class TestController : ControllerBase
{
}

也可以在startupConfigureServices()里面进行添加,表示全局注册

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
        options.Filters.Add(new WebApplication2.Utility.Filter.TestActionFilterAttribute()));
}

执行结果

OnActionExecuting执行了
Action执行
OnActionExecuted执行了

多个过滤器执行时,会保持着先进后出的原则,例如

public class TestActionFilterAttribute : Attribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine("OnActionExecuted1执行了");
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine("OnActionExecuting1执行了");
    }
}
public class TestActionFilterAttribute2 : Attribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine("OnActionExecuted2执行了");
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine("OnActionExecuting2执行了");
    }
}

执行结果为

OnActionExecuting1执行了
OnActionExecuting2执行了
Action执行
OnActionExecuted2执行了
OnActionExecuted1执行了

除了直接实现Filter接口,.Net Core同时提供了一些基于特性的过滤器,我们可以继承相应的特性来实现自定义的过滤器。这些特性包括ActionFilterAttributeExceptionFilterAttributeResultFilterAttributeFormatFilterAttributeServiceFilterAttributeTypeFilterAttribute

Filter通过注解的话如何注入实例呢

如果特性类的构造函数有参的话特性也是需要带入参数的并且只能是常量,那么如何注入呢?

  1. ServiceFilter 需要在容器中注册过滤器
[ServiceFilter(typeof(TestActionFilterAttribute))]
  1. TypeFilter 通过使用Microsoft.Extensions.DependencyInjection.ObjectFactory对指定的过滤器类型进行实例化因此不需要注册容器
 [TypeFilter(typeof(TestActionFilterAttribute))]
  1. IFilterFactory 需要自己手动实现工厂类(其实就是第一种的实现方式,也需要在容器中注册过滤器)
public class CustomFilterFactory : Attribute,IFilterFactory
{
    private Type _filterType = null;
    public CustomFilterFactory(Type type)
    {
        this._filterType = type;
    }
    public bool IsReusable => true;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
       return (IFilterMetadata)serviceProvider.GetService(this._filterType);
      //  throw new NotImplementedException();
    }
}
08-25 12:48