【容易混淆】Nest模块内外及依赖注入

news/2024/10/20 4:08:35/

Nest模块内外及依赖注入

  • 从@Module装饰器去考虑
  • 从模块内外考虑
  • 模块外如何注入
  • 容易混淆的点

在文档的 Exception filters即异常过滤器的 Binding filters一节中,提到了 全局绑定的两个方式:

// 第一种
@@filename(main)
async function bootstrap() {const app = await NestFactory.create(AppModule);app.useGlobalFilters(new HttpExceptionFilter());await app.listen(3000);
}
bootstrap();
// 第二种
@@filename(app.module)
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';@Module({providers: [{provide: APP_FILTER,useClass: HttpExceptionFilter,},],
})
export class AppModule {}

在文档中提到第一种方式无法注入依赖项,怎么去理解?

从@Module装饰器去考虑

首先要理解依赖注入的流程,分为两步:
第一步是在构造函数中传入依赖项,代码如下:

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {constructor(private readonly logger: LoggerService) {} } // 注入依赖项

第二步是在@Module模块中注册,代码如下:

@Module({imports: [HttpModule],providers: [LoggerService,{provide: APP_FILTER,useClass: HttpExceptionFilter,},],
})
export class AppModule {}

只有在@Module中注册,Nest才会进行自动解析和注入,就无需程序员手动去创造实例对象,这一整个流程就是依赖注入

现在再来看一个例子,区分手动注入以及自动即依赖注入的区别:

// 与第一步对照,可看到这里是手动创建实例(简单来说就是需要自己new一个实例)
export class UserController {private logger = new LoggerService();
}

现在回过头来看第一种方式,代码如下:

@@filename(main)
async function bootstrap() {const app = await NestFactory.create(AppModule);app.useGlobalFilters(new HttpExceptionFilter());await app.listen(3000);
}
bootstrap();

这段代码没有经过@Module,自然无法执行依赖注入,即使类HttpExceptionFilrer的代码中包含构造函数有注入的类,即代码如下的情况:

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {constructor(private readonly logger: LoggerService) {} // 包含注入的类catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp();const response = ctx.getResponse();const request = ctx.getRequest();const status = exception.getStatus();this.logger.error(`Error at ${request.url}: ${exception.message}`,JSON.stringify(exception.getResponse()),);response.status(status).json({statusCode: status,message: exception.message,});}
}

当程序运行时,app会实例化HttpExceptionFilter(),但因为由于LoggerService未被注入,运行时会抛出错误,提示 this.logger未定义

所以如果想在全局配置异常过滤器且需要依赖注入,那么就只能在根模块使用工厂函数的方式去挂载,即第二种方式,代码如下:

@Module({imports: [HttpModule],providers: [LoggerService,{provide: APP_FILTER,useClass: HttpExceptionFilter,},],
})
export class AppModule {}

很明显,这里使用了@Module,那么Nest就会执行依赖注入和解析

从模块内外考虑

在文档中提到:In terms of dependency injection, global filters registered from outside of any module (with useGlobalFilters() as in the example above) cannot inject dependencies since this is done outside the context of any module.
这里的outside of any module即模块外,在整个项目中,模块指的就是xxx.module.ts,而很明显,文件main.ts不属于任何的模块,所以称之为模块外模块外的代码中没有@Module,所以不能实现依赖注入

模块外如何注入

在上面提到模块外不能依赖注入,但在文档的Exception filters最后一节即Inheritance中又出现了一个在模块外注入的例子,在文中提到Global filters can extend the base filter. This can be done in either of two ways. The first method is to inject the HttpAdapter reference when instantiating the custom global filter
说可以在实例化自定义全局过滤器时注入HttpAdapter引用,用于实现全局筛选器扩展基本筛选器,代码如下:

async function bootstrap() {const app = await NestFactory.create(AppModule);const { httpAdapter } = app.get(HttpAdapterHost); // 手动获取依赖项app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));await app.listen(3000);
}
bootstrap();

这又如何理解?

其实这不算依赖注入,至少我们可以通过①模块内or外②是否有经过@Module去辨别这不算,这种方式可以称之为依赖获取

关键在于const { httpAdapter } = app.get(HttpAdapterHost);这行代码,从NestJS容器中手动获取 HttpAdapterHost服务,然后传递给AllExceptionsFilter的构造函数

这是手动传入,而不是由@Module自动注入并解析!

容易混淆的点

以为在main.tsnew AllExceptionsFilter(httpAdapter)这行代码是依赖注入,即以为传入服务就是依赖注入,本质上是没有理解依赖注入是由@Module来管理和实现的

以为在main.tsnew AllExceptionsFilter()中不能传入任何服务


http://www.ppmy.cn/news/1540414.html

相关文章

SpringBoot实现桂林旅游的智能推荐

3系统分析 3.1可行性分析 通过对本桂林旅游景点导游平台实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本桂林旅游景点导游平台采用SSM框架,JAVA作…

Python 使用 Jarvis 算法或包装的凸包(Convex Hull using Jarvis’ Algorithm or Wrapping)

给定平面中的一组点,该集合的凸包是包含该集合所有点的最小凸多边形。 我们强烈建议您先阅读以下文章。 如何检查两个给定的线段是否相交? c https://blog.csdn.net/hefeng_aspnet/article/details/141713655 java https://blog.csdn.net/hefeng_aspne…

Docker system

docker system --help siqialiyun-sh-001:~/images$ sudo docker system --helpUsage: docker system COMMANDManage DockerCommands:df Show docker disk usage(显示docker磁盘使用情况)events Get real time events from the server(从服务器获取实时事件)in…

(echarts)折线图初始渲染canvas特别窄问题

(echarts)折线图初始渲染canvas特别窄问题 自行解决使用方法: this.$nextTick(() > {this.showView(this.queryId) })解决后: 搜索建议:

R语言绘图——文本注释

在R语言中,文本注释通常用于向图形中添加注释或说明,可以通过一些函数在图形上添加文字、标签等。以下是R中处理文本注释的常见函数和方法。 0x01 text()函数 一、常见语法 text() 函数允许你在绘图的指定位置上添加文字注释。其常用语法如下&#xf…

这种V带的无极变速能用在新能源汽车上吧?

CVT的无极变速器的结构能用在电动汽车上吗?

[机器视觉]basler相机使用SN编号打开相机和采集

背景分析 在项目中是用basler相机采图时,一般用的比较多的遍历相机,然后使用CreateFirstDevice这个函数获取相机,有些时候可能需要同时连接多个相机,这里一般是遍历后,再循环打开相机,根据打开相机的SN号确…

Golang | Leetcode Golang题解之第480题滑动窗口中位数

题目: 题解: type hp struct {sort.IntSlicesize int } func (h *hp) Push(v interface{}) { h.IntSlice append(h.IntSlice, v.(int)) } func (h *hp) Pop() interface{} { a : h.IntSlice; v : a[len(a)-1]; h.IntSlice a[:len(a)-1]; return v }…