概念
Nest 中的 管道(Pipe) 是一个带 @Injectable() 且实现了 PipeTransform 接口的类。管道在「控制器方法被调用前」执行,所以非常适合做入参的校验与预处理。

职责
- 验证 ------ 数据不符合规则就直接抛异常,请求进不到控制器;
- 转换 ------ 把入参改成我们想要的形式(字符串→数字、普通对象→DTO 实例等)。
内置管道(开箱即用)
Nest 自带 10 个左右的常用管道,下面 3 个最常见:
| 管道 | 作用 | 用法示例 |
|---|---|---|
| ParseIntPipe | 把字符串参数转成整数,失败抛 400 | @Param('id', ParseIntPipe) id: number |
| ValidationPipe | 结合 class-validator,自动按 DTO 装饰器校验整个 Body | @Body() dto: CreateUserDto + 全局 app.useGlobalPipes(new ValidationPipe()) |
| DefaultValuePipe | 没传参时塞一个默认值 | @Query('page', new DefaultValuePipe(1)) page: number |
自定义管道
- 实现 PipeTransform 接口,写 transform(value, metadata) 方法;
- 返回「转换后的值」或「原值」;
- 校验失败直接抛 BadRequestException 即可。
小栗子
生成管道
bash
nest g pipe common/pipes/hex-to-int
逻辑实现
ts
// src/common/pipes/hex-to-int.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class HexToIntPipe implements PipeTransform<string, number> {
transform(value: string, meta: ArgumentMetadata): number {
const intVal = parseInt(value, 16); // 按 16 进制解析
if (isNaN(intVal)) {
throw new BadRequestException(`"${value}" 不是合法 16 进制字符串`);
}
return intVal; // 返回转换后的 10 进制数字
}
}
控制器使用
ts
@Get('color/:hex')
getColor(@Param('hex', HexToIntPipe) color: number) {
// color 已经是数字,例如 0xff → 255
return { color, binary: color.toString(2) };
}
类验证器
Nest 与 class-validator 库能很好地协同工作。这个强大的库允许你使用基于装饰器的验证。基于装饰器的验证功能极其强大,特别是与 Nest 的管道功能结合使用时,因为我们可以访问被处理属性的 metatype。
使用
bash
npm i --save class-validator class-transformer
1. class-validator ------ 「校验」专家
作用:在运行期根据装饰器规则,验证一个对象里的字段是否合法;不合法就抛详细的错误信息。
核心装饰器(常用 10 个)
| 装饰器 | 含义 |
|---|---|
@IsString() |
必须是字符串 |
@IsInt() / @IsNumber() |
必须是整数 / 数字 |
@IsEmail() |
合法邮箱格式 |
@IsOptional() |
允许缺失或 undefined |
@IsNotEmpty() |
不能是空字符串、null、undefined |
@Min(0) / @Max(100) |
数值上下界 |
@Length(2, 20) |
字符串长度 |
@IsEnum(Role) |
值必须在枚举里 |
@IsArray() / @ArrayMinSize(1) |
数组及其长度 |
@ValidateNested() + @Type(()=>ChildDto) |
嵌套对象/数组校验 |
小栗子
创建DTO并贴装饰器
ts
import { IsNotEmpty, IsString, Length } from 'class-validator';
export class SigninUserDto {
@IsString()
@IsNotEmpty()
@Length(3, 10, {
// $value: 用户传入的值
// $property: 属性名
// $target: 当前类
// $constraint1: 3 最小长度
// $constraint2: 10 最大长度
message: '用户名长度必须在3到10个字符之间,当前传递的值为: $value',
})
username: string;
@IsString()
@IsNotEmpty()
@Length(6, 10, {
message: '密码长度必须在6到10个字符之间,当前传递的值为: $value',
})
password: string;
}
控制器中接入DTO
ts
@Post('signin')
signin(@Body() loginDto: SigninUserDto) {
const {username, password } = loginDto
return this.authService.signin(username, password);
}
全局开启ValidationPipe(main.ts)
ts
// 全局拦截器
app.useGlobalPipes(new ValidationPipe({
// 去除实体中不存在的字段
// whitelist: true,
// 额外报错,防止客户端乱传。
//forbidNonWhitelisted: true
}))
2. class-transformer ------ 「转换」专家
作用:把普通对象(例如 JSON 解析后都是字面量)转换成类实例,并可在转换时做字段改名、类型强制、嵌套实例化等操作;同时提供反向能力(把实例变回普通对象)。
核心API
| API | 作用 |
|---|---|
plainToInstance(Target, plain) |
把纯对象 → 类实例 |
instanceToPlain(instance) |
把类实例 → 纯对象 |
plainToClassFromExist(existing, plain) |
在已有实例上合并数据 |
expose / exclude 系列装饰器 |
控制序列化/反序列化时哪些字段可见 |
常用装饰器
| 装饰器 | 含义 |
|---|---|
@Type(()=>Type) |
指定字段要实例化的目标类(解决嵌套) |
@Transform(({value})=>fn(value)) |
自定义转换函数 |
@Expose() / @Exclude() |
显式暴露或排除字段 |
@Expose({name:'_id'}) |
把后端字段 _id 映射到前端 id |
小栗子
定义实体类
ts
// user.entity.ts
import { Expose, Type, Transform } from 'class-transformer';
export class User {
@Expose({ name: '_id' }) // 数据库字段叫 _id,序列化后叫 id
id: string;
email: string;
@Exclude() // 返回前端时永远去掉密码
password: string;
@Type(() => Date) // 把时间戳/字符串 → Date 实例
createdAt: Date;
@Transform(({ value }) => value ?? 'USER') // 缺省值
role: string;
}
手动转换(在 service 里)
ts
const user = plainToInstance(User, userRecord); // userRecord 是 Mongo 原始对象
return instanceToPlain(user); // 返回给控制器 → 自动去掉 password 等
配合 ValidationPipe 嵌套校验
ts
export class CreateOrderDto {
@ValidateNested()
@Type(() => OrderItemDto) // 必须同时加,否则 class-validator 拿不到实例
items: OrderItemDto[];
}
前端 JSON 数据校验与转换流程
流程说明:
- 前端发送 JSON 数据到后端。
- 后端使用 ValidationPipe 处理数据。
- ValidationPipe 内部先调用 class-transformer 将普通对象转换为 DTO 实例。
- 然后使用 class-validator 对 DTO 实例进行校验。
- 控制器接收到已转换且校验通过的数据。
- 业务逻辑处理完成后,使用 class-transformer 将实体转换回普通对象。
- 最终将普通对象返回给前端。