NestJS 管道
基础语法
- 用 @Injectable() 装饰类;
- 实现 PipeTransform 接口;
- 实现 transform 方法(处理转换 / 验证逻辑);
参数说明
shell
# 核心方法
transform(value: any, metadata: ArgumentMetadata): any | Promise<any>
# 参数说明
value:待处理的参数值(如 URL 参数、请求体、查询参数)
metadata:参数元数据,包含:
type:参数类型(body/query/param/custom)
metatype:参数的类型(如 String/Number/ 自定义 DTO)
data:参数名(如 @Param('id') 中的 id)验证型管道
聚焦单个参数的精细化验证,同步验证管道(手机号格式校验)
- 定义管道
ts
// src/common/pipes/phone-number.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class PhoneNumberPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
// 仅处理 @Body('phone') 对应的参数
if (metadata.type === 'body' && metadata.data === 'phone') {
const phoneRegex = /^1[3-9]\d{9}$/;
if (!value || !phoneRegex.test(value)) {
throw new BadRequestException('手机号格式错误(需为11位中国大陆手机号)');
}
}
// 合法则透传值
return value;
}
}- 使用管道
ts
@Post()
create(
@Body('phone', PhoneNumberPipe) phone: string,
@Body() createUserDto: CreateUserDto,
) {
return { phone, createUserDto };
}转换型管道
将前端传入的日期字符串(如 '2025-12-03')转换为 Date 对象
- 定义管道
ts
// src/common/pipes/parse-date.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseDatePipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (!value) return value;
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new BadRequestException(`无效的日期格式:${value}`);
}
return date;
}
}- 使用管道
ts
@Get('stats')
getStats(@Query('startDate', ParseDatePipe) startDate: Date) {
return { startDate, type: typeof startDate }; // startDate 为 Date 实例
}异步管道
transform 方法支持返回 Promise,适用于需要异步操作的场景(如数据库校验)
- 定义管道
ts
// src/user/pipes/user-exists.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata, NotFoundException } from '@nestjs/common';
import { UserService } from '../user.service';
@Injectable()
export class UserExistsPipe implements PipeTransform {
// 依赖注入 UserService(管道支持完整的依赖注入能力)
constructor(private readonly userService: UserService) {}
async transform(value: string, metadata: ArgumentMetadata) {
// 仅处理 @Param('userId') 对应的参数
if (metadata.type === 'param' && metadata.data === 'userId') {
const user = await this.userService.findById(value);
if (!user) {
throw new NotFoundException(`用户 ${value} 不存在`);
}
}
return value;
}
}- 使用管道
ts
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get(':userId')
findOne(@Param('userId', UserExistsPipe) userId: string) {
return this.userService.findById(userId);
}
}内置管道
- 内置管道
shell
# 转换 & 验证
ParseIntPipe 将字符串参数转换为整数,失败则抛 400 异常
ParseFloatPipe 将字符串参数转换为浮点数;
ParseBoolPipe 将字符串参数转换为布尔值(支持 'true'/'false'、'1'/'0' 等)
ParseArrayPipe 将字符串转换为数组,转化不了则报异常
# 验证
ValidationPipe 基于 class-validator 校验 DTO 字段(最常用)
ParseUUIDPipe 校验参数是否为 UUID(支持指定版本)
# 默认值
DefaultValuePipe 为未传递的参数设置默认值(需配合其他管道使用)- 使用
ts
import { Controller, Get, Param, Query, ParseIntPipe, DefaultValuePipe } from '@nestjs/common';
@Controller('users')
export class UsersController {
// 路由参数转换为整数
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return { id, type: typeof id }; // id 已确保是 number 类型
}
// 查询参数设置默认值 + 转换为整数
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
@Query('size', new DefaultValuePipe(10), ParseIntPipe) size: number
) {
return { page, size };
}
}管道执行顺序
全局管道 → 模块管道 → 控制器管道 → 方法管道 → 参数管道;
同级别多个管道按注册顺序执行;
管道执行是串行的,前一个管道的输出是后一个管道的输入。
ts
// 执行顺序:PipeA → PipeB → ParseIntPipe。
// 自定义管道 A
@Injectable()
export class PipeA implements PipeTransform {
transform(value: any) {
console.log('PipeA 执行');
return value + '_A';
}
}
// 自定义管道 B
@Injectable()
export class PipeB implements PipeTransform {
transform(value: any) {
console.log('PipeB 执行');
return value + '_B';
}
}
// 控制器使用
@Controller('test')
@UsePipes(PipeA) // 控制器级别
export class TestController {
@Get()
@UsePipes(PipeB) // 方法级别
test(@Query('name', ParseIntPipe) name: string) {
console.log('最终值:', name);
return name;
}
}