Nest 基础概要
update 2023.05.09
入口 main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule)
// 可通过 app.use 添加全局配置
app.use(logger) // 全局 logger 中间件
app.useGlobalFilters(new HttpExceptionFilter()) // 全局异常过滤器
app.useGlobalPipes(new ValidationPipe()) // 全局管道处理
app.useGlobalGuards(new RolesGuards()) // 全局守卫
app.useGlobalInterceptors(new LoggingInterceptor()) // 全局拦截器
await app.listen(3000)
}
模块 Module
- 每个应用至少有一个根模块,作为应用的入口
- 使用
@Module装饰 nest g module cats
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService] // 导出共享
})
export class CatsModule {}
请求链路流程
- 由 客户端 发起请求开始,一系列的处理流程顺序
Client Side -> Middleware -> Guards -> Interceptors -> pipes -> [Controllers -> Providers]: 具体的执行函数Exception Filter: 中间件、守卫、拦截器、管道以及函数执行时抛出的异常,执行顺序均会从抛出错误的那一刻转至异常拦截器
1. 中间件 Middleware
- 函数,可以访问到请求和响应对象
- 通过
next()将控制权传递给下一个中间件函数 Nest可以通过函数或者类实现中间件
// 类的方式, 使用 @Injectable 装饰, 并实现 NestMiddleware 接口
import { Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response } from 'express'
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('Request...')
next()
}
}
// 函数的方式
export function logger(req, res, next) {
console.log(`Request...`)
next()
}
2. 守卫 Guards
- 中间件逻辑之 后、拦截器/管道之 前 执行
- 决定请求是否要被控制器处理,一般使用在权限、角色场景中
- 相比于中间件(调用
next()任务完成),守卫还需要关注上下游
// 使用 @Injectable 装饰, 实现 CanActivate 接口
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { Observable } from 'rxjs'
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return true
}
}
// 通过 @Guards(RolesGuard) 使用
- 此时已经有用了守卫,但没有执行上下文
- 应该有一些需要访问到的权限类型
- 通常使用
@SetMetadata()对控制器添加元数据
@Post()
@SetMetadata('role', ['admin'])
async creat() { ... }
// 实现一个自定义装饰器
import { SetMetadata } from '@nestjs/common'
export const Roles = (...roles: string[]) => SetMetadata('roles', roles)
// 使用
@Post()
@Roles('admin')
async create() { ... }
3. 拦截器 Interceptors
- 使用
@Injectable装饰,必须实现NestInterceptor接口 - 在函数执行前后绑定额外的逻辑
- 转换一个函数返回值
- 转换函数抛出的异常
- 扩展基础函数行为
- 根据特定条件完全重写一个函数
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'
import { Observable } from 'rxjs'
import { tap } from 'rxjs/operators'
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...')
const now = Date.now()
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
)
}
}
// 通过 @UseInterceptors(LoggingInterceptor) 使用
// 将所有响应中出现的 null 转换为 ''
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
@Injectable()
export class ExcludeNullInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(map(value => value === null ? '' : value ))
}
}
4. 管道 Pipes
- 控制器接收请求 前 处理,偏向于服务端
interceptor start -> pipe validate -> handler -> interceptor end- 使用
@Injectable装饰,必须实现PipeTransform接口 - 转换输入数据为目标格式 && 验证输入数据
- 管道会在异常范围内执行 - 异常处理层可以处理管道异常
- 内置
ValidationPipe和ParseIntPipe
// 配合第三方库使用
// yarn add class-validator class-transformer
import { validate } from 'class-validator'
import { plainToClass } from 'class-transformer'
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToClass(metatype, value)
const errors = await validate(object)
if (errors.length > 0) {
throw new BadRequestException('Validation failed')
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object]
return !types.includes(metatype)
}
}
// 通过 @UsePipes(ValidationPipe) 使用
5. 控制器 Controller
- 处理客户端请求并发送响应内容
- 路由决定具体处理的请求(调用
Service来处理) @Controller() && @Get()、@Post()...nest g controller cats
import { Controller, Get } from '@nestjs/common'
@Controller('cats')
export class CatsController {
@Get()
async findAll(): string {
return 'This action returns all cats'
}
}
6. 提供者 Providers
- 控制反转
IOC模式中的依赖注入特性 - 使用
@Injectable()装饰 - 通常只关注于处理业务逻辑,供
Controller调用 nest g service cats
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
// ./interfaces/cat.interface
export interface Cat {
name: string
age: number
breed: string
}
// 将Service注入到控制器中
import { CatsService } from './cats.service'
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
异常过滤器 Exception Filter
- 默认情况下会被 全局 异常过滤器
HttpException或它的子类处理 - 若未识别,会返回给客户端
500, Internet server error的报错
// @Catch 装饰, 并实现 ExceptionFilter 接口
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common'
import { Request, Response } from 'express'
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
const request = ctx.getRequest<Request>()
const status = exception.getStatus()
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
})
}
}
// 通过 @UseFilters(HttpExceptionFilter) 使用
// 方法作用域、控制器作用域、全局作用域...