前言

我们在使用ASP.NET Core进行服务端应用开发的时候,或多或少都会涉及到使用Filter的场景。Filter简单来说是Action的拦截器,它可以在Action执行之前或者之后对请求信息进行处理。我们知道.Net Core默认是提供了IOC的功能,而且IOC是.Net Core的核心,.Net Core的底层基本上是基于IOC构建起来的,但是默认情况下自带的IOC不支持属性注入功能,但是我们在定义或使用Filter的时候有时候不得不针对某个Controller或Action,这种情况下我们不得不将Filter作为Attribute标记到Controller或Action上面,但是有时候Filter是需要通过构造函数注入依赖关系的,这个时候就有了一点小小的冲突,就是我们不得不解决在Controller或Action上使用Filter的时候,想办法去构建Filter的实例。本篇文章不是一篇讲解ASP.NET Core如何使用过滤器Filter的文章,而是探究一下Filter与IOC的奇妙关系的。

简单示例

咱们上面说过了,我们所用的过滤器即Filter,无论如何都是需要去解决与IOC的关系的,特别是在当Filter作用到某些具体的Controller或Action上的时候。因为直接标记的话必须要给构造函数传递初始化参数,但是这些参数是需要通过DI注入进去的,而不是手动传递。微软给我们提供了解决方案来解决这个问题,那就是使用TypeFilterAttributeServiceFilterAttribute,关于这两个Attribute使用的方式,咱们先通过简单的示例演示一下。首先定义一个Filter,模拟一下需要注入的场景

public class MySampleActionFilter : Attribute, IActionFilter
{private readonly IPersonService _personService;private readonly ILogger<MySampleActionFilter> _logger;//模拟需要注入一些依赖关系public MySampleActionFilter(IPersonService personService, ILogger<MySampleActionFilter> logger){_personService = personService;_logger = logger;_logger.LogInformation($"MySampleActionFilter.Ctor {DateTime.Now:yyyyMMddHHmmssffff}");}public void OnActionExecuted(ActionExecutedContext context){Person personService = _personService.GetPerson(1);_logger.LogInformation($"TraceId=[{context.HttpContext.TraceIdentifier}] MySampleActionFilter.OnActionExecuted ");}public void OnActionExecuting(ActionExecutingContext context){_logger.LogInformation($"TraceId=[{context.HttpContext.TraceIdentifier}] MySampleActionFilter.OnActionExecuting ");}
}

这里的日志功能ILogger在ASP.Net Core底层已经默认注入了,我们还模拟依赖了一些业务的场景,因此我们需要注入一些业务依赖,比如我们这里的PersonService。

public void ConfigureServices(IServiceCollection services)
{//模拟注册一下业务依赖services.AddScoped<IPersonService,PersonService>();services.AddControllers();
}

单独使用Filter

这里我们先来演示一下单独在某些Controller或Action上使用Filter的情况,我们先来定义一个Action来模拟一下Filter的使用,由于Filter通过构造函数依赖了一下具体的服务所以我们先选择使用TypeFilterAttribute来演示,具体使用方式如下

[Route("api/[controller]/[action]")]
[ApiController]
public class PersonController : ControllerBase
{private readonly List<Person> _persons;public PersonController(){//模拟一下数据_persons = new List<Person>{new Person{ Id=1,Name="张三" },new Person{ Id=2,Name="李四" },new Person{ Id=3,Name="王五" }};}[HttpGet]//这里我们先通过TypeFilter的方式来使用定义的MySampleActionFilter[TypeFilter(typeof(MySampleActionFilter))]public List<Person> GetPersons(){return _persons;}
}

然后我们运行起来示例,模拟请求一下GetPersons这个Action看一下效果,因为我们在定义的Filter中记录了日志信息,因此请求完成之后在控制台会打印出如下信息

info: Web5Test.MySampleActionFilter[0]MySampleActionFilter.Ctor 202110121820482450
info: Web5Test.MySampleActionFilter[0]TraceId=[0HMCDD7ARPKDK:00000003] MySampleActionFilter.OnActionExecuting
info: Web5Test.MySampleActionFilter[0]TraceId=[0HMCDD7ARPKDK:00000003] MySampleActionFilter.OnActionExecuted

这个时候我们将TypeFilterAttribute替换为ServiceFilterAttribute来看一下效果,替换后的Action是这个样子的

[HttpGet]
[ServiceFilter(typeof(MySampleActionFilter))]
public List<Person> GetPersons()
{return _persons;
}

然后我们再来请求一下GetPersons这个Action,这个时候我们发现抛出了一个InvalidOperationException的异常,异常信息大致如下

System.InvalidOperationException: No service for type 'Web5Test.MySampleActionFilter' has been registered.

从这个异常信息我们可以看出我们自定义的MySampleActionFilter过滤器需要注册到IOC中去,所以我们需要注册一下

public void ConfigureServices(IServiceCollection services)
{//模拟注册一下业务依赖services.AddScoped<IPersonService,PersonService>();//注册自定义的MySampleActionFilterservices.AddScoped<MySampleActionFilter>();services.AddControllers();
}

做了如上的修改之后,我们再次启动项目请求一下GetPersons这个Action,这个时候MySampleActionFilter可以正常工作了。

这里简单的说明一下关于需要注册Filter的生命周期时,如果你不知道该注册成哪种生命周期的话那就注册成成Scope,这个是一种比较合理的方式,也就是和Controller生命周期保持一致每次请求创建一个实例即可。注册成单例的话很多时候会因为使用不当出现一些问题。

通过上面的演示我们大概了解了TypeFilterAttributeServiceFilterAttribute的使用方式和区别。

•使用TypeFilterAttribute的时候我们的Filter过滤器是不需要注册到IOC中去的,因为它使用Microsoft.Extensions.DependencyInjection.ObjectFactory对Filte过滤器类型进行实例化•使用ServiceFilterAttribute的时候我们需要提前将我们定义的Filter注册到IOC容器中去,因为它使用容器来创建Filter的实例

全局注册的场景

很多时候呢,我们是针对全局使用Filter对所有的或者绝大多数的Action请求进行处理,这个时候我们会全局注册Filter而不需要在每个Controller或Action上一一注解。这个时候也涉及到关于Filter本身是否需要注册到IOC容器中的情况,这个地方需要注意的是Filter不是必须的需要托管到IOC容器当中去,但是一旦托管到IOC容器当中就需要注意不同注册Filter的方式,首先我们来看一下不将Filter注册到IOC的使用方式,还是那个示例

public void ConfigureServices(IServiceCollection services)
{services.AddScoped<IPersonService,PersonService>();services.AddControllers(options => {options.Filters.Add<MySampleActionFilter>();});
}

只需要把自定义的MySampleActionFilter依赖的服务提前注册到IOC容器即可不需要多余的操作,这个时候MySampleActionFilter就可以正常的工作。还有一种方式就是你想让IOC容器去托管自定义的Filter,这个时候我们需要将Filter注册到容器中去,当然声明周期我们还是选择Scope,这个时候我们需要注意一下注册全局Filter的方式了,如下所示

public void ConfigureServices(IServiceCollection services)
{services.AddScoped<IPersonService,PersonService>();services.AddScoped<MySampleActionFilter>();services.AddControllers(options => {//这里需要注意注册Filter的方法应使用AddServiceoptions.Filters.AddService<MySampleActionFilter>();});
}

如上面代码所示,为了能让Filter的实例来自于IOC容器,在注册全局Filter的时候我们应使用AddService方法完成注册,否则的话即使使用Add方法不会报错但是在IOC中你只能注册了个寂寞,总结一下全局注册的时候

•如果你不想将全局注册的Filter托管到IOC容器中,那么需要使用Add方法,这样的话Filter实例则不会通过IOC容器创建•如果你想控制Filter实例的生命周期,则需要将Filter提前注册到IOC容器中去,这个时候注册全局Filter的时候就需要使用AddService方法,如果使用了AddService方法,但是你没有在IOC中注册Filter,则会抛出异常

源码探究

上面我们已经演示了将Filter托管到IOC容器和不使用IOC容器的使用方式,这方面微软考虑的也是很周到,不过就是容易让新手犯错。如果能熟练掌握,或者理解其中的工作原理的话,还是可以更好的使用这些,并且微软还为我们提供了一套灵活的扩展方式。想要更好的了解它们的工作方式,我们还得在源码下手。

TypeFilterAttribute

首先我们来看一下TypeFilterAttribute的源码,我们知道在某个Action上使用TypeFilterAttribute的时候是不要求将Filter注册到IOC中去的,因为这个时候Filter的实例是通过ObjectFactory创建出来的。在开始之前我们需要知道一个常识那就是在ASP.NET Core上我们所使用的Filter都必须要实现IFilterMetadata接口,这是ASP.NET Core底层知道Filter的唯一凭证,比如我们上面自定义的MySampleActionFilter是实现了IActionFilter接口,那么IActionFilter肯定是直接或间接的实现了IFilterMetadata接口,我们可以看一下IActionFilter接口的定义[点击查看源码

ASP.NET Core Filter与IOC的羁绊相关推荐

  1. ASP.NET Core Controller与IOC的羁绊

    前言 看到标题可能大家会有所疑问Controller和IOC能有啥羁绊,但是我还是拒绝当一个标题党的.相信有很大一部分人已经知道了这么一个结论,默认情况下ASP.NET Core的Controller ...

  2. ASP.NET Core Filter如何支持依赖注入

    概述 通过使用 ASP.NET Core 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码.内置筛选器处理任务,例如:授权(防止用户访问未获授权的资源).响应缓存(对请求管道进行短路出路,以 ...

  3. Asp.Net Core Filter 深入浅出的那些事-AOP

    一.前言 在分享ASP.NET Core Filter 使用之前,先来谈谈AOP,什么是AOP 呢? AOP全称Aspect Oriented Programming意为面向切面编程,也叫做面向方法编 ...

  4. ASP.NET Core中使用IOC三部曲(三.采用替换后的Autofac来实现AOP拦截)

    上一篇ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)我们讲了如何将默认的容器替换为Autofac,并使用属性注入.那么这一篇我们就来讲讲如何利用A ...

  5. ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)

    上一篇ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器) ,我们说过ASP.NET Core中自带的IOC容器是属于轻量级的,功能并不是很多,只是提供了基础 ...

  6. ASP.NET Core Filter

    ASP.NET Core Filter Filter 简介 ExceptionFilter ActionFilter ActionFilter 实现请求限流 UseExceptionHandler - ...

  7. ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器)

    前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期.这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 正文 今 ...

  8. 【asp.net core 系列】14 .net core 中的IOC

    0.前言 通过前面几篇,我们了解到了如何实现项目的基本架构:数据源.路由设置.加密以及身份验证.那么在实现的时候,我们还会遇到这样的一个问题:当我们业务类和数据源越来越多的时候,我们无法通过普通的构造 ...

  9. java按需读取word文件_干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结...

    我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章<.NET CORE与Spring Boot编写控制台程序应有 ...

最新文章

  1. win7系统开启休眠按钮
  2. 2021年计算机三级新题型,2021年如何通过计算机三级考试的经验
  3. oracle命令行查看编码,Oracle数据库查看编码和修改编码
  4. CUDA11.1安装教程(python3.8)
  5. 模态识别在计算机视觉应用,相关性学习在计算机视觉任务中的研究与应用
  6. rtsp连接断开_live555_RTSP连接建立以及请求消息处理过程
  7. mysql中的字符是多长_mysql中的varchar到底能存多长的字符
  8. 基于任务的异步模式(TAP)
  9. 标准模板库(STL)之 vector 列传 (二)
  10. jquery表单选择器input、:text、:password、:radio、:checkbox、:submit、:reset、:image、:button、:file、:hidden
  11. 厂商服务器存储默认管理口登录信息 默认IP、用户名、密码
  12. Arduino 开发 — Arduino 函数库
  13. python监控服务器cpu温度实例_用python访问CPU温度
  14. linux命令 执行间隙,linux 定时执行任务 at atq atrm命令的使用
  15. 云原生背景下的运维价值思考与实践
  16. 常用计算机控制芯片有哪些,电脑基本芯片的认识的常用知识介绍
  17. java pdf stamper_使用pdfstamper(Itext)将页码添加到pdf
  18. 微信小程序简单实现两列瀑布流布局页面
  19. android一行三列,Android -- listview实现一行多列效果
  20. java 发邮件 新浪,发邮件时终于可以通过sina的smtp验证了(附代码)

热门文章

  1. linux操作命令等积累
  2. Android API 中文(14) —— ViewStub
  3. python函数 global_**Python的函数参数传递 和 global
  4. MSSqlServer基础学习01
  5. BZOJ 1047: [HAOI2007]理想的正方形 单调队列瞎搞
  6. yum 下载RPM包而不进行安装
  7. php OpenSSL 加解密
  8. Leetcode 动态规划 Trapping Rain Water
  9. Mathematica修改默认字体
  10. 文本框输入值文字消失常用的两种方法