该文章内容在文章 Nestjs 和 Prisma 实现 Restful Api的基础上实现,如有需要,请先搜索查看基础文章
第二章
参数验证与转换
为了执行输入验证,您将使用NestJS Pipes。管道对路由处理程序正在处理的参数进行操作。Nest在路由处理程序之前调用一个管道,该管道接收用于路由处理程序的参数。管道可以做很多事情,比如验证输入、向输入添加字段等等。管道类似于中间件,但管道的作用域仅限于处理输入参数。NestJS提供了一些开箱即用的管道,但是您也可以创建自己的管道
管道有两个典型的用例:
- 验证:评估输入的数据,如果有效,则不加修改地传递;否则,当数据不正确时抛出异常。
- 转换:将输入数据转换为所需的形式(例如,从字符串转换为整数)。
全局设置ValidationPipe
在 NestJS 中,可以使用内置的 ValidationPipe 来进行输入验证。ValidationPipe 提供了一种便捷的方法,能够强制对所有来自客户端的请求数据进行验证。验证规则通过 class-validator 包的装饰器来声明,装饰器用于定义 DTO 类中的验证规则,以确保传入的数据符合预期的格式和类型。
首先我们需要安装2个库
1
| pnpm install class-validator class-transformer
|
在 main.ts 引入 ValidationPipe,然后使用 app.useGlobalPipes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ValidationPipe } from '@nestjs/common';
async function bootstrap() { const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
const config = new DocumentBuilder() .setTitle('Median') .setDescription('The Median API description') .setVersion('0.1') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document);
await app.listen(3000); } bootstrap();
|
向CreateArticleDto添加验证规则
使用 class-validator 库给 CreateArticleDto 添加验证装饰器。
打开 src/articles/dto/create-article.dto.ts 文件,替换成以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
import { ApiProperty } from '@nestjs/swagger'; import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength, } from 'class-validator';
export class CreateArticleDto { @IsString() @IsNotEmpty() @MinLength(5) @ApiProperty() title: string;
@IsString() @IsOptional() @IsNotEmpty() @MaxLength(300) @ApiProperty({ required: false }) description?: string;
@IsString() @IsNotEmpty() @ApiProperty() body: string;
@IsBoolean() @IsOptional() @ApiProperty({ required: false, default: false }) published?: boolean = false; }
|
articles.service 中 create 方法接受的参数就是 CreateArticleDto 类型,我们来新增一篇文章来测试一下。
1 2 3 4 5 6
| { "title": "Temp", "description": "Learn about input validation", "body": "Input validation is...", "published": false }
|
接口返回的内容如下:
1 2 3 4 5 6 7
| { "message": [ "title must be longer than or equal to 5 characters" ], "error": "Bad Request", "statusCode": 400 }
|
这说明添加的验证规则生效了。客户端发起了一个 post 请求,ValidationPipe 验证参数未通过直接就返回给前端了,并没有到后续的路由。
从客户端请求中删除不必要的属性
如果我们在新建文章的时候加入 CreateArticleDto 中未定义的其他的属性,也是能新增成功的。但这很有可能会造成错误,比如新增下面这样的数据
1 2 3 4 5 6 7 8
| { "title": "example-title", "description": "example-description", "body": "example-body", "published": true, "createdAt": "2010-06-08T18:20:29.309Z", "updatedAt": "2021-06-02T18:20:29.310Z" }
|
通常来说,新增文件的 createdAt 是 ORM 自动生成的当前时间,updateAt 也是自动生成的。当我们传递的额外参数通过了校验,这是非常危险的,所幸 Nestjs 为我们提供了白名单的机制,只需要在初始化 ValidationPipe 的时候加上 ** whitelist:true ** 就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ValidationPipe } from '@nestjs/common';
async function bootstrap() { const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
const config = new DocumentBuilder() .setTitle('Median') .setDescription('The Median API description') .setVersion('0.1') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document);
await app.listen(3000); } bootstrap();
|
现在 ValidationPipe 将自动删除所有非白名单属性,也就是没有添加验证装饰器的额外参数都会被删除。
使用 ParseIntPipe 转换动态URL路径参数
目前我们的 api 接口中有很多利用到了路径参数,例如 GET /articels/:id, 路径参数获取到是 string 类型,我们需要转为 number 类型,通过 id 查询数据库。
1 2 3 4 5 6 7
|
@Delete(':id') @ApiOkResponse({ type: ArticleEntity }) remove(@Param('id') id: string) { return this.articlesService.remove(+id); }
|
由于id被定义为字符串类型,因此Swagger API在生成的API文档中也将此参数作为字符串记录。这是不直观和不正确的。
使用 Nestjs 内置的 ParseIntPipe 管道可以在路由处理之前,拦截字符串参数并转化为整数,并且修正 Swagger 文档的参数类型。
修改我们的控制器代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
import { Controller, Get, Post, Body, Patch, Param, Delete, NotFoundException, ParseIntPipe, } from '@nestjs/common';
export class ArticlesController {
@Get(':id') @ApiOkResponse({ type: ArticleEntity }) findOne(@Param('id', ParseIntPipe) id: number) { return this.articlesService.findOne(id); }
@Patch(':id') @ApiCreatedResponse({ type: ArticleEntity }) update( @Param('id', ParseIntPipe) id: number, @Body() updateArticleDto: UpdateArticleDto, ) { return this.articlesService.update(id, updateArticleDto); }
@Delete(':id') @ApiOkResponse({ type: ArticleEntity }) remove(@Param('id', ParseIntPipe) id: number) { return this.articlesService.remove(id); } }
|
刷新 Swagger 接口文档,id 参数修改成了 number 类型。
通过文章的学习,我们完成了以下功能:
- 集成 ValidationPipe 来验证参数类型
- 去除不必要的额外参数
- 使用 ParseIntPipe 将 string 转化成 number 类型