5.2-主要概念
Create by fall on 14 Dec 2021 Recently revised in 22 Aug 2024
Controller
控制器
控制器处理传入的请求和想客户端返回的响应,
路由机制控制那个控制器处理那个请求。通常每个控制器有多个路由,不同的路由进行不同的操作。
/* cats.controller.ts */
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get('/around') // 用于告诉 Nest 为 HTTP 请求的特定端点创建处理程序
findAll(): string {
return 'This action returns all cats';
}
}
路由路径 ?一个处理程序的路由路径是通过连接为控制器 (Controller) 声明的(可选)前缀和请求装饰器中指定的任何路径来确定的。
Provider
Controller 会调用 provider 中的函数,
provider 包括 service
// 创建 code.service.ts
import { Injectable } from '@nestjs/common'
@Injectable()
export class CodeService {
findAll () {
return [{
name: '代码片段1',
content: 'const a = 106',
}]
}
}
调用 service
// code.controller.ts
import { CodeService } from './code.service'
@Controller('code')
export class CodeController {
//
constructor (private readonly codeService: CodeService) {}
@Get()
findAll () {
return this.codeService.findAll()
}
}
Modules
import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Global() // 将此模块使用全局进行暴露
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
全局 module 应该只被注册一次
dynamic modules
import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';
@Module({
providers: [Connection],
exports: [Connection],
})
export class DatabaseModule {
static forRoot(entities = [], options?): DynamicModule {
const providers = createDatabaseProviders(options, entities);
return {
module: DatabaseModule,
providers: providers,
exports: providers,
};
}
}
Middleware
中间件,可以做哪些内容?
- 在特定请求中,执行任意代码
- 对返回内容进行统一包装
- 终止 request-response 循环
- 进入下一个中间件
- 中间件如果没有结束 request-response循环,必须调用
next()
,否则将会一直挂起
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
// 配置中间件
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
// 如果要使用多个中间件可以
// .apply(cors(), helmet(), logger)
.exclude( // 排除一些路由
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
// 还可以绑定 Controller
// .forRoutes(CatsController)
}
}
函数式写法
如果你的中间件没有任何依赖,请使用函数式中间件来代替
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
意外捕获
https://docs.nestjs.com/exception-filters
默认就有一个全局意外捕获
{
"message": "Cannot POST /faker",
"error": "Not Found",
"statusCode": 404
}
自定义全局捕获
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
管道
pipe
- 将数据转换为想要的类型
- 验证,验证数据是否有效
当在 pipe 中抛出了一个错误,之后的 controller 将不会执行
nest 提供的 pipe line
ValidationPipe
ParseIntPipe
ParseFloatPipe
ParseBoolPipe
ParseArrayPipe
ParseUUIDPipe
,验证ParseUUIDPipe()
你在使用 UUID 3, 4 or 5 的版本ParseEnumPipe
DefaultValuePipe
ParseFilePipe
使用
@Controller('code')
export class CodeController {
constructor (private readonly codeService: CodeService) {}
@Get(':id')
findOne (@Param('id', new ParseUUIDPipe()) id: string) {
// request
console.log('🚀 ~ CodeController ~ findOne ~ request:', randomUUID())
return this.codeService.findOne(id)
}
}
@Controller('code')
export class CodeController {
constructor (private readonly codeService: CodeService) {}@Post()
@HttpCode(200)
create (@Body('codeType', ParseTypePipe) createCodeDto: CreateCodeDto) {
console.log('🚀 ~ CodeController', createCodeDto)
return this.codeService.create(createCodeDto)
}
}
为什么不是中间件?
中间件确实可以工作,但是中间件并不知道你调用了哪个参数,使用了哪个方法因此很难创造出通用的中间件
增强 dto
@Controller('code')
export class CodeController {
constructor (private readonly codeService: CodeService) {}
@Patch(':id')
@UseInterceptors(NoFilesInterceptor()) // 接收 FormData
update (@Param('id') id: string, @Body(new ValidationPipe()) updateCodeDto: UpdateCodeDto) {
console.log('🚀 ~ CodeController ~ update ~ param:', updateCodeDto)
return this.codeService.update(id, updateCodeDto)
}
}
守卫
guards
定义一个守卫:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Observable } from 'rxjs'
@Injectable()
export class RulesGuard implements CanActivate {
canActivate (
context:ExecutionContext
):boolean|Promise<boolean>|Observable<boolean> {
const request = context.switchToHttp().getRequest()
console.log('🚀 ~ AuthGuard ~ request:', request)
// validateRequest(request)
return true
}
}
使用一个守卫
import { Controller, UseGuards } from '@nestjs/common'
@Controller('code')
@UseGuards(RulesGuard)
export class CodeController {}
// 也可以创建一个实例
@Controller('code')
@UseGuards(new RulesGuard())
export class CodeController {}
如果要使用全局守卫,使用 app 上面的 useGlobalGuards()
方法
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());
拦截器
Interceptors
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } 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`)),
);
}
}
自定义路由装饰器
Custom route decorators
import { Reflector } from '@nestjs/core';
export const Roles = Reflector.createDecorator<string[]>();