FORMA

管道 (Pipes) 与数据验证

管道在入参进入处理器前做转换校验。见 guards 之前的请求链。

管道(Pipe)在 NestJS 中用于处理请求数据(如请求体、查询参数、路由参数等)的转换验证。它在数据被控制器方法处理之前执行,可以:

  • 转换:将输入数据转换为所需的类型(如字符串转数字)。
  • 验证:检查输入数据是否符合预设规则,若不符合则抛出异常(通常是 BadRequestException)。

一、内置管道

NestJS 提供了一系列内置管道,覆盖常见的数据转换和验证场景。

管道作用使用示例
ValidationPipe基于 DTO 类和 class-validator 装饰器进行验证@Body(ValidationPipe) dto: CreateUserDto
ParseIntPipe将字符串转换为整数,失败则抛出 BadRequestException@Param('id', ParseIntPipe) id: number
ParseFloatPipe将字符串转换为浮点数@Query('price', ParseFloatPipe) price: number
ParseBoolPipe将字符串转换为布尔值('true'/'false'@Query('active', ParseBoolPipe) active: boolean
ParseUUIDPipe验证字符串是否为 UUID 格式(支持版本指定)@Param('uuid', new ParseUUIDPipe({ version: '4' })) uuid: string
ParseEnumPipe验证字符串是否属于指定的枚举值@Query('role', new ParseEnumPipe(RoleEnum)) role: RoleEnum
DefaultValuePipe为缺失的参数提供默认值(常与 ParseIntPipe 等配合)@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number

示例

ts
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  // id 已经确保为 number 类型,且是有效整数
  return this.service.findOne(id);
}

二、数据验证:ValidationPipe + class-validator

ValidationPipe 是 NestJS 中最强大的验证管道,它使用 class-validatorclass-transformer 库,通过 DTO(数据传输对象)类中的装饰器声明验证规则。

1. 安装依赖

bash
npm install class-validator class-transformer

2. 定义 DTO 类并添加验证装饰器

ts
import { IsString, IsEmail, IsInt, Min, Max, IsOptional } from "class-validator";

export class CreateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;

  @IsInt()
  @Min(18)
  @Max(120)
  age: number;

  @IsOptional()
  @IsString()
  bio?: string;
}

3. 在控制器中使用 ValidationPipe

ts
@Post()
create(@Body(ValidationPipe) createUserDto: CreateUserDto) {
  // createUserDto 已经通过验证,类型安全
  return this.usersService.create(createUserDto);
}

如果请求数据不满足验证规则,NestJS 会自动返回 400 Bad Request,并包含详细的错误信息(哪些字段违反了哪些规则)。

4. ValidationPipe 常用选项

ValidationPipe 可以配置多种行为来优化验证过程和安全性。

选项类型描述
whitelistboolean自动剥离 DTO 中未定义装饰器的属性(防止多余字段注入)
forbidNonWhitelistedboolean如果存在白名单之外的属性,直接抛错(拒绝请求)
transformboolean自动将请求数据转换为 DTO 类的实例(类型转换)
disableErrorMessagesboolean生产环境隐藏详细错误信息(只返回字段错误列表)
skipMissingPropertiesboolean是否忽略未出现在请求中的可选字段(默认 false)

配置示例(全局或局部):

ts
@Post()
@UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }))
create(@Body() createUserDto: CreateUserDto) {
  // ...
}
  • whitelist: true:自动去除 DTO 中未定义装饰器的属性。
  • forbidNonWhitelisted: true:当请求包含未定义的属性时,拒绝请求并返回错误。
  • transform: true:将请求的普通对象转换为 DTO 类的实例,便于类型转换(例如字符串 "18" 自动转为数字 18)。

三、创建自定义管道

当内置管道无法满足需求时,可以实现 PipeTransform 接口创建自定义管道。

ts
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from "@nestjs/common";

@Injectable()
export class CustomParseIntPipe implements PipeTransform<string, number> {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException(`Invalid integer: ${value}`);
    }
    return val;
  }
}

transform 方法的参数:

  • value:传入的原始值(如查询字符串、参数值)。
  • metadata:包含 type(参数类型,如 'body''query''param')、metatype(DTO 类)、data(参数名)等信息。

使用自定义管道

ts
@Get(':id')
findOne(@Param('id', CustomParseIntPipe) id: number) {
  // id 已经是有效整数
}

四、全局管道注册

为减少重复代码,可以将 ValidationPipe 或其他管道注册为全局管道,自动应用于所有入参。

ts
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ValidationPipe } from "@nestjs/common";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
  await app.listen(3000);
}
bootstrap();

全局管道会作用于所有控制器的方法参数(包括 @Body()@Query()@Param())。但也可通过 @UsePipes() 在局部覆盖或组合。

注意:全局管道如果使用 ValidationPipe,需要确保 class-validator 和 class-transformer 已安装。

五、数据转换的另一种方式:@Transform()

class-transformer 提供的 @Transform() 装饰器可以在 DTO 类中进行更高级的数据转换。

ts
import { Transform } from "class-transformer";

export class CreateUserDto {
  @Transform(({ value }) => value.trim())
  @IsString()
  name: string;
}

通过 transform: true 开启 ValidationPipe 的转换功能后,上述转换会自动执行。

六、最佳实践

  • 使用 DTO + class-validator:将验证规则声明在 DTO 中,保持控制器整洁。
  • 开启 whitelistforbidNonWhitelisted:防止未预期的属性注入,增强安全性。
  • 开启 transform:自动类型转换,避免手动 parseInt 等操作。
  • 全局注册 ValidationPipe:避免在每个控制器重复写 @Body(ValidationPipe)
  • 自定义管道用于复杂转换:例如从数据库加载实体、数据格式解析等。

总结

概念作用
ParseIntPipe简单类型转换
ValidationPipe + class-validator基于 DTO 的强大验证
whitelist / forbidNonWhitelisted防止多余字段注入
transform: true自动类型转换
自定义管道复用复杂的转换/验证逻辑
全局管道统一应用所有请求的验证规则

通过合理使用管道,可以大幅减少控制器中的样板代码,提升应用的数据安全性和可维护性。

参考文献

以下链接在编写时均可正常访问:

资料说明
NestJS 文档官方
Pipes本章主题
Request lifecycle执行顺序