NestJS 自定义管道使用场景
转换:字符串转数字
- 定义
ts
// src/pipes/parse-int.pipe.ts
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException(`"${value}" 不是有效的数字`);
}
return val;
}
}- 使用
ts
// src/controllers/user.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { ParseIntPipe } from '../pipes/parse-int.pipe';
@Controller('users')
export class UserController {
// URL参数id强制转为数字
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return { id, message: `获取用户 ${id} 成功` };
}
}验证:结构化数据验证
处理 POST/PUT 请求体(Body)的结构化验证(如必填项、格式、长度),是最常用的管道场景。Nest 内置 ValidationPipe,结合 class-validator 实现。
- 安装依赖
shell
npm install class-validator class-transformer --save- 定义 DTO 并添加验证规则
ts
// src/dtos/create-user.dto.ts
import { IsString, IsEmail, MinLength, IsNotEmpty } from 'class-validator';
export class CreateUserDto {
@IsString()
@IsNotEmpty({ message: '用户名不能为空' })
@MinLength(3, { message: '用户名长度≥3' })
username: string;
@IsEmail({}, { message: '邮箱格式错误' })
email: string;
@IsString()
@MinLength(6, { message: '密码长度≥6' })
password: string;
}- 使用验证管道
ts
// src/controllers/user.controller.ts
import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';
import { CreateUserDto } from '../dtos/create-user.dto';
@Controller('users')
export class UserController {
@Post()
@UsePipes(
new ValidationPipe({
whitelist: true, // 过滤DTO中未定义的属性
forbidNonWhitelisted: true, // 存在未知属性时抛异常
transform: true, // 自动将请求体转为DTO实例
})
)
create(@Body() createUserDto: CreateUserDto) {
return { message: '创建成功', data: createUserDto };
}
}验证:年龄验证
- 定义年龄验证管道(仅允许≥18 岁)
ts
// src/pipes/age-validation.pipe.ts
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
@Injectable()
export class AgeValidationPipe implements PipeTransform {
transform(value: any) {
const age = parseInt(value, 10);
if (isNaN(age)) throw new BadRequestException('年龄必须是数字');
if (age < 18) throw new BadRequestException('年龄必须≥18岁');
return age;
}
}- 使用
ts
@Post('register')
createUser(
@Body('age', AgeValidationPipe) age: number,
@Body() createUserDto: CreateUserDto,
) {
return { ...createUserDto, age };
}验证:订单状态
- 定义订单状态验证管道(仅支持指定状态)
ts
// src/pipes/order-status-validation.pipe.ts
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
const ALLOWED_STATUSES = ['pending', 'paid', 'shipped', 'completed'];
@Injectable()
export class OrderStatusValidationPipe implements PipeTransform {
transform(value: any) {
if (!ALLOWED_STATUSES.includes(value)) {
throw new BadRequestException(`状态仅支持:${ALLOWED_STATUSES.join(', ')}`);
}
return value;
}
}- 使用
ts
// src/controllers/order.controller.ts
import { Controller, Patch, Param, Body } from '@nestjs/common';
import { OrderStatusValidationPipe } from '../pipes/order-status-validation.pipe';
import { ParseIntPipe } from '../pipes/parse-int.pipe';
@Controller('orders')
export class OrderController {
@Patch(':id/status')
updateStatus(
@Param('id', ParseIntPipe) id: number,
@Body('status', OrderStatusValidationPipe) status: string
) {
return { id, status, message: `订单状态更新为 ${status}` };
}
}自定义错误处理
默认的 BadRequestException 无法满足自定义错误格式(如错误码),需在管道中抛出自定义业务异常
- 自定义错误异常
ts
// src/exceptions/business.exception.ts
import { HttpException, HttpStatus } from '@nestjs/common';
export class BusinessException extends HttpException {
constructor(
message: string,
private readonly errorCode: number
) {
super({ message, errorCode }, HttpStatus.BAD_REQUEST);
}
}- 管道中使用自定义异常
ts
// src/pipes/phone-validation.pipe.ts
import { PipeTransform, Injectable } from '@nestjs/common';
import { BusinessException } from '../exceptions/business.exception';
const PHONE_REGEX = /^1[3-9]\d{9}$/; // 手机号正则
@Injectable()
export class PhoneValidationPipe implements PipeTransform {
transform(value: any) {
if (!PHONE_REGEX.test(value)) {
throw new BusinessException('手机号格式错误', 10001); // 自定义错误码
}
return value;
}
}- 使用
ts
@Post('bind-phone')
bindPhone(@Body('phone', PhoneValidationPipe) phone: string) {
return { message: '绑定成功', phone };
}
// 结果:
// {
// "message": "手机号格式错误",
// "errorCode": 10001
// }