Provider的多种注入
-
Nest实现了IOC容器,从入口开始扫描,分析Module之间的引用关系,对象之间的依赖关系,自动把provider注入到目标对象中。
-
provider的几种注入方式:
-
- 简写方式: AppService => { provide: AppService(token), useClass: AppService }, 在AppController中构造器中注入AppService对象, 就会自动注入
-
- 属性注入: 通过@Inject指定注入的provider的token即可。
-
@Inject(AppService) // provide 指定的token private readonly appService: AppService;
-
- 构造器注入: 通过构造器注入指定provider的token即可。
-
{provide: "app_service", useClass: AppService} // 字符串, 需要使用@Inject进行指定token constructor(@Inject('app_service') private readonly appService: AppService) {}
-
- 除了指定class外, 还可以直接指定一个值没让IOC来注入
-
{provide: "app_service", useValue: {name: "aaa", age: 20}} // 直接指定一个值 constructor(@Inject('app_service') private readonly appService: AppService) {}
-
-
provider的值可能是动态的,Nest的支持方式
-
{provide: "person2", useFactory() => {name: "bbb", desc: "ccc"}} // 动态产生的时候使用的是useFactory函数动态生成一个对象 - useFactory支持通过参数注入别的provider, 通过inject声明两个token,一个是字符串的person, 两一个是class的AppService
{ provide: 'person3', useFactory(person: { name: string }, appService: AppService) { return { name: person.name, age: appService.getHello(), }; }, inject: ['person', AppService], }, -
-
- useFactory支持异步async,nest会等拿到异步方法的结果之后再注入
-
- provider还可以通过useExisting来指定别名。{provide: "person4", useExisting: "person2"}
-
全局模块
- 模块通过exports导出provider,另一个模块需要imports才能使用这些rpovider,但是如果这个模块被很多模块依赖了,每次导入的时候就会很麻烦,所以需要设置为全局模块
(@Global()), 这个时候就不需要在别的模进行imports带入了
生命周期
-
在Nest启动的时候, 会递归解析Module依赖,然后扫描其中的provider, controller,注入它的依赖,全部解析后,会监听网络端口,开始处理请求。
-
nest中的生命周期
-
- 创建(OnModuleInit)和启动(OnApplicationBoostrap)声明周期: 首先,递归初始化模块,会依次调用模块的controller, provider的onModuleInit方法,然后在调用module的onModuleInit方法。全部初始化完成之后,再依次调用模块内的controller, provider的onApplicationBoostrap方法,然后在调用module的onApplicationBoostrap方法。然后开始监听网络端口。
-
- 关闭(OnModuleDestroy)和销毁(BeforeApplitionShutdown)声明周期: 先调用模块controller, provider的onModuleDestory方法,然后调用调用module的onModuleDestory方法,然后依次调用模块内的controller, provider的beforeApplicationShutdown方法,调用module的beforeApplicationShutdown方法, 最后关闭网络端口。最后调用每个模块的onApplicationShutdown方法。
-
beforeApplicationShutdown和onApplicationShutdown的区别: beforeApplicationShutdown是在关闭之前调用的,onApplicationShutdown是在关闭之后调用的。beforeApplicationShutdown是可以拿到single系统信号。这些终止信息是别的进行传递过来的,让系统做一些销毁的事情。比如k8s管理容器的时候, 可以通过这个信号来通知它。
-
一般都是通过moduleRef取出一些provider来销毁,比如关闭连接等。moduleRef就是当前模块的引用。
this.moduleRef.get(AppService)调用get方法,传入token,就可以获取到对应的provider对象。
-
Aop架构
-
Aop的好处是可以把一些通用逻辑分离到切面中,保持业务逻辑纯粹性,这样切换逻辑就可以复用,还可以动态的增删。
-
- 中间件Middleware
-
全局中间件:在main中使用app.use注入的中间件, 在handler之前增加一些可复用的逻辑。
-
路由中间件:
nest g middleware log-
@Injectable() export class LogMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log('log middleware'); next(); } } export class PersonModule implements NestModule { configure(consumer: MiddlewareConsumer) { // 匹配到的路由才生效 consumer.apply(LogMiddleware).forRoutes('person/log*'); } }
-
-
- Guard
- 路由守卫, 可以用在调用某个Controller之前判断权限,返回true或者false来决定是否放行。使用@UseGuards给某个路由添加权限校验
- 全局守卫:
app.useGlobalGuards(new AuthGuard())或者使用AppModule中提供者模式 {provide: APP_GUARD, useClass: AuthGuard}, 全局守卫是在所有路由之前执行的, 通过provide注入的方式Guard在IOC容器中,可以注入别的provider. 而Global的方式就不能访问到。
-
-
- Interceptor
- 可以在目标controller方法前后加入一些逻辑。
-
- Interceptor和Middleware的区别
- 主要是参数不同,interceptor可以调用controller和handler. controller和handler加一些metadata,这种就只有interceptor和guard才可以取出参数,middleware不能。
-
- Interceptor是支持单路由启用
@UseInterceptors(TimeInterceptor)。
- Interceptor是支持单路由启用
-
- Interceptor是支持Conltroller级别启用。作用于这个controller的handler方法.
-
- Interceptor是支持全局启用
app.useGlobalInterceptors(new TimeInterceptor()),作用于全部的controller。还可以像Guard一样在AppModule中指定提供者使用IOC容器注入的方式{ptovide:APP_INTERCEPTOR, useClass: TimeInterceptor}
- Interceptor是支持全局启用
-
- Pipe
-
除了路由的权限控制、目标 Controller 之前之后的处理这些都是通用逻辑外,对参数的处理也是一个通用的逻辑,所以 Nest 也抽出了对应的切面.Pipe 是管道的意思,用来对参数做一些检验和转换.
-
Pipe 要实现 PipeTransform 接口,实现 transform 方法,里面可以对传入的参数值 value 做参数验证,比如格式、类型是否正确,不正确就抛出异常。也可以做转换,返回转换后的值
-
Nest 内置了一些 Pipe,从名字就能看出它们的意思:
- ValidationPipe
- ParseIntPipe
- ParseBoolPipe
- ParseArrayPipe
- ParseUUIDPipe
- DefaultValuePipe
- ParseEnumPipe
- ParseFloatPipe
- ParseFilePipe
-
Pipe针对某个参数生效,或者针对某个controller生效 或者全局生效 {provide: APP_PIPE, useClass: ValidationPipe}.
-
不管Pipe, Guard, Interceptor还是最终的controller,过程中都可以抛出一些异常。如何对某种异常做出某种响应呢。这种异常到响应的映射也是一种通用逻辑。Nest提供了ExceptionFilter接口来实现。
6. ExceptionFilter
-
ExceptionFilter 可以对抛出的异常做处理,返回对应的响应:
-
Nest 内置了很多 http 相关的异常,都是 HttpException 的子类: BadRequestException UnauthorizedException NotFoundException ForbiddenException NotAcceptableException RequestTimeoutException ConflictException GoneException PayloadTooLargeException UnsupportedMediaTypeException UnprocessableException InternalServerErrorException NotImplementedException BadGatewayException ServiceUnavailableException GatewayTimeoutException
-
自定义个异常处理类,实现ExceptionFilter接口。
-
export class ForbiddenException implements HttpException { super("没有权限", HttpStatus.FORBIDDEN) } // Nest 通过这样的方式实现了异常到响应的对应关系,代码里只要抛出不同的异常,就会返回对应的响应,很方便。 -
ExceptionFilter 也可以选择全局生效或者某个路由生效或者某个Controller生效。
几种AOP的机制。
- Middleware、Guard、Pipe、Interceptor、ExceptionFilter 都可以透明的添加某种处理逻辑到某个路由或者全部路由,这就是 AOP 的好处。