如何在NestJS中链式调用验证与转换管道?
NestJS验证与转换管道链式调用
我想要为NestJS控制器链式调用两个管道:一个用于验证请求体是否符合指定DTO类型,另一个用于将该DTO转换为服务方法所需的特定类型。
当前控制器:
@Post() @UseGuards(JwtAuthGuard, ProductOwnershipGuard) async create(@Body() createAuctionDto: CreateAuctionDto) { return this.auctionsService.create(createAuctionDto); }
期望流程
JwtAuthGuard校验用户是否已登录ProductOwnershipGuard校验请求体中的product_id对应的产品是否属于当前用户ValidateAuctionPipe校验用户传入的请求体是否符合CreateAuctionDto类型TransformAuctionPipe将CreateAuctionDto转换为Auction类型,通过关联产品(前文提到的product_id)添加部分属性- Controller 接收
Auction类型的变量,并将其传入auctionsService
已尝试的解决方案
首先,我使用了全局ValidationPipe,因此@Body() createAuctionDto: CreateAuctionDto会自动验证请求体是否符合CreateAuctionDto,无需额外注解。
方案1
@Post() @UseGuards(JwtAuthGuard, ProductOwnershipGuard) async create(@Body(TransformAuctionPipe) auction: Auction) { return this.auctionsService.create(createAuctionDto); }
问题:全局ValidationPipe会校验请求体是否符合Auction类型,而非CreateAuctionDto。
方案2
@Post() @UseGuards(JwtAuthGuard, ProductOwnershipGuard) async create(@Body(new ValidationPipe()) createAuctionDto: CreateAuctionDto, @Body(TransformAuctionPipe) auction: Auction) { return this.auctionsService.create(auction); }
问题:能正确验证createAuctionDto,但TransformAuctionPipe接收到的是Auction类型的空对象。
方案3
@Post() @UseGuards(JwtAuthGuard, ProductOwnershipGuard) async create(@Body(new ValidationPipe({ expectedType: CreateAuctionDto }), TransformAuctionPipe) auction: Auction) { return this.auctionsService.create(auction); }
问题:完全没有对请求体进行CreateAuctionDto验证。
方案4
尝试使用@UsePipes()装饰器替代在@Body()中传入管道,但同样无效。
管道代码
ValidateDtoPipe
/* eslint-disable @typescript-eslint/ban-types */ import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; import { validate } from 'class-validator'; import { plainToInstance } from 'class-transformer'; @Injectable() export class ValidateDtoPipe implements PipeTransform<any> { async transform(value: any, { metatype }: ArgumentMetadata) { if (!metatype || !this.toValidate(metatype)) { return value; } const object = plainToInstance(metatype, value); const errors = await validate(object); if (errors.length > 0) { throw new BadRequestException('Validation failed'); } return value; } private toValidate(metatype: Function): boolean { const types: Function[] = [String, Boolean, Number, Array, Object]; return !types.includes(metatype); } }
TransformAuctionPipe
import { ArgumentMetadata, Injectable, NotFoundException, PipeTransform } from '@nestjs/common'; import { ProductsService } from 'src/api/products/products.service'; import { CreateAuctionDto } from '../dto/create-auction.dto'; import { Auction } from '../entities/auction.entity'; import { EAuctionStatus } from '../types/auction.types'; /** * This pipe transforms CreateAuctionDto to Auction, * by fetching associated Product and adding its properties to CreateAuctionDto * */ @Injectable() export class TransformAuctionPipe implements PipeTransform<CreateAuctionDto, Promise<Auction>> { constructor(private readonly productsService: ProductsService) {} async transform(createAuctionDto: CreateAuctionDto, metadata: ArgumentMetadata): Promise<Auction> { const product = await this.productsService.findOneById(createAuctionDto.product_id); if (!product) { throw new NotFoundException('Product not found.'); } const auctionStatus: EAuctionStatus = +createAuctionDto.start_at <= Date.now() ? EAuctionStatus.ACTIVE : EAuctionStatus.PENDING; const { name, description, price } = product; const auction = new Auction({ ...createAuctionDto, name, description, price, product, status: auctionStatus }); return auction; } }
内容的提问来源于stack exchange,提问作者fufuj
相关产品推荐
相关产品推荐

