Note
全局注册
介绍守卫、拦截器、管道、异常过滤器不同方式全局注册的区别
发布于 更新于
区别
守卫、拦截器、管道、异常过滤器全局注册的方式有两种,一是在 main.ts 中使用 useGlobal*() ,二是在任意模块中的 provide 中使用 APP_* + useClass 。它们的区别是由谁创建和管理实例。在 main.ts 中是你自己创建和管理实例,这样不能获得 Nest 自动依赖注入,而模块中是 Nest 创建的实例,它可以自动查找并注入依赖。两种注册方式对比:
| 对比项 | main.ts 的 useGlobal*() |
模块中的 APP_* + useClass |
|---|---|---|
| 实例由谁创建 | 自己 new |
Nest 创建 |
| 自动依赖注入 | 不支持 | 支持 |
| 生命周期由谁管理 | 自己管理 | Nest 容器管理 |
| 测试模块中是否自动生效 | 通常不会,需要执行 bootstrap 配置 | 会随模块加载 |
| 模块化程度 | 启动文件集中配置 | 可以放在能力所属模块 |
| 适合场景 | 无依赖、配置简单的内置组件 | 需要注入服务的自定义组件 |
手动创建实例
如果需要使用依赖,在 main.ts 手动创建的实例需要自己传递参数:
全局拦截器使用到了 Reflector
@Injectable()
export class ResponseInterceptor {
constructor(private readonly reflector: Reflector) {}
}
会报错显示缺少 Reflector 参数
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(
new ResponseInterceptor(), // 缺少 Reflector
);
需要自己手动添加
const reflector = app.get(Reflector);
app.useGlobalInterceptors(
new ResponseInterceptor(
reflector,
),
);
Nest创建实例
即使这些 provider 写在某个子模块里,它们仍然是全局的,并不是只对该模块生效。官方建议把注册代码放在这个增强器所属的模块中。只是为了规范,这类全局注册我们会放到 APPModule 下。
@Injectable()
export class ResponseInterceptor {
constructor(private readonly reflector: Reflector) {}
}
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor,
},
],
})
export class AppModule {}
使用main.ts 全局注册的时机
对于没有业务依赖、只需要简单配置的内置组件,使用 main.ts 注册反而更直观
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}),
);
总结
不需要注入项目服务、配置简单,完全可以使用 main.ts 注册,需要注入依赖时,如 Reflector、Logger、ConfigService 等,优先使用 APP_* 。