.net 温故知新【16】:Asp.Net Core WebAPI 筛选器

发布时间 2023-12-18 12:19:45作者: XSpringSun

一、筛选器

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

这即是我们经常听到的面向切面编程AOP(Aspect Oriented Programming)技术,AOP通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

筛选器在 ASP.NET Core 操作调用管道(有时称为筛选器管道)内运行。 筛选器管道在 ASP.NET Core 选择了要执行的操作之后运行:
image

Asp.Net Core 关注的切面点 包括错误处理、缓存、配置、授权和日志记录筛选器,这个是说通过筛选器可以实现对以上关注点的一些操作。

在Asp.Net Core中有如下几种类型的筛选器:

image

其中部分是内置筛选器,比如授权,响应缓存已经帮我们内置进了框架,我们只需要配置即可使用;其他筛选器是可以自定义处理逻辑的。

下图展示了筛选器类型在筛选器管道中的交互方式和执行顺序:

image

二、操作型筛选器

第一部分主要是对筛选器的一个梳理,有些重点的提炼,详情查看文档,因为文档部分理解起来比较晦涩,比如关注点是关注点,知识说筛选器可以对这些关注点启到作用,筛选器是固定的几种,不要被文档中的这种描述搞晕了,一会儿有这几种,怎么到下面又是另外几种,要注意区分重点。

操作筛选器可以实现接口IActionFilter,在接口中有两个方法,OnActionExecuting 在调用操作方法之前执行。 OnActionExecuted 在操作方法返回之后执行。

  • 先建WebAPI项目 WebAPI_Filter
  • 建一个 FilterController,并创建Get请求Test
namespace WebAPI_Filter.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class FilterController : ControllerBase
    {
        [HttpGet]
        public string Test()
        {
            return "测试Filter!";
        }
    }
}
  • 创建ActionFilter 筛选器
namespace WebAPI_Filter.Filter
{
    public class MyActionFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl()+ "  执行之后!");
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  执行之前!");
        }
    }
}
  • 在Program.cs里面添加筛选器
    image

执行测试接口
image

三、筛选器作用域和执行顺序

上面直接在Program.cs里面添加筛选器的方式称为全局筛选器,所有控制器、操作都会受全局筛选器影响。还有一种筛选器实现方式是属性筛选器,通过继承属性类然后将属性标签放置在控制器或者操作上。

新建两个属性类MyAttributeFilter 用于Controller控制器类,MyOPAttributeFilter用于操作方法上。

    public class MyAttributeFilter: ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  控制器之后-筛选器属性!");
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  控制器之前-筛选器属性!");
        }
    }


    public class MyOPAttributeFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  操作之后-筛选器属性!");
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  操作之前-筛选器属性!");
        }
    }

image

加上之前的全局筛选器,我们一共有三个作用域的筛选器,现在我们测试看看筛选器的执行顺序。
image

则可总结出不同作用域筛选器的执行顺序:

全局筛选器的 before 代码。
	控制器筛选器的 before 代码。
		操作方法筛选器的 before 代码。
		操作方法筛选器的 after 代码。
	控制器筛选器的 after 代码。
全局筛选器的 after 代码。

当然可以通过 Order 属性来确定执行顺序,在全局或者属性筛选器里面设置 Order 值,值越小执行优先级越高。

image

四、筛选器依赖注入

可按类型或实例添加筛选器。 如果添加实例,该实例将用于每个请求。

其中builder.Services.AddControllers(options => options.Filters.Add<MyActionFilter>())即为按实例添加,该MyActionFilter用于每个请求。

如果添加类型,则将激活该类型。 激活类型的筛选器意味着:第一种是为每个请求创建一个实例,第二种依赖关系注入 (DI) 将填充所有构造函数依赖项。

上面位置我们是为每个请求创建一个实例,这样的话无法使用依赖注入体系为我们自动注入,因为因为属性在应用时必须提供自己的构造函数参数,该参数需要手动指定。

比如我们想在操作方法的MyOPAttributeFilter筛选属性 注入IHostEnvironment:

    public class MyOPAttributeFilter : ActionFilterAttribute
    {
        IHostEnvironment hostEnvironment;

        public MyOPAttributeFilter(IHostEnvironment _hostEnvironment)
        {
            hostEnvironment = _hostEnvironment;
        }
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  操作之后-筛选器属性!");
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            Console.WriteLine(context.HttpContext.Request.GetDisplayUrl() + "  操作之前-筛选器属性!");
            //打印环境变量
            Console.WriteLine(hostEnvironment.EnvironmentName);
        }
    }

这个时候直接就报错提示需要参数,而我们想的是通过依赖注入配置。

image

框架提供以下筛选器支持从 DI 提供的构造函数依赖项:

  • ServiceFilterAttribute
  • TypeFilterAttribute
  • 在属性上实现 IFilterFactory。

TypeFilterAttribute:不会直接从 DI 容器解析其类型。Microsoft.Extensions.DependencyInjection.ObjectFactory 对类型进行实例化,所以不需要先将MyOPAttributeFilter加入容器,直接使用:

[TypeFilter(typeof(MyOPAttributeFilter))]

image

ServiceFilterAttribute 使用需要先将MyOPAttributeFilter注入到容器,然后再使用。

image

以上就是关于AOP切面编程和筛选器的梳理,其他类型的筛选器和细节可查询官方文档:ASP.NET Core 中的筛选器