返回文章列表 →

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_* 。