如何在NestJS中使用DTO实现对象数组的参数校验
问题原因及修复方案
1. 前置依赖与全局管道配置校验
首先确保你已经安装了两个必要依赖:
npm install class-validator class-transformer
同时在main.ts中开启全局校验管道,否则DTO校验规则不会生效:
import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 必须添加全局校验管道,开启自动转换 app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); await app.listen(3000); } bootstrap();
2. 核心错误修复
你当前代码存在多个结构不匹配、规则缺失的问题,是校验不生效的核心原因:
- 提交的是直接的对象数组,但定义的
PostTriviaScoreDto是包裹了items字段的对象,结构完全不匹配,校验规则无法对应到传入字段 - 使用了
ParseArrayPipe但接收参数的类型写的是单个DTO对象,不符合数组传参要求 answered_in_time字段只加了非空校验,没有数值类型校验,所以传入字符串也不会被拦截- Controller调用Service时传入了未定义的
postTriviaScoreParamsDto变量,运行时会直接报错
有两种修复方向,选其中一种即可:
方向一:保留当前直接传数组的请求结构
修改post-trivia-score.dto.ts,导出内层的TriviaScore类:
import { Type } from 'class-transformer'; import { IsNotEmpty, IsUUID, IsNumber } from 'class-validator'; // 导出这个类给ParseArrayPipe使用 export class TriviaScore { @IsNotEmpty() @IsUUID() question_id: string; @IsNotEmpty() @IsUUID() selected_option_id: string; @IsNotEmpty() @IsNumber() // 新增数值类型校验 answered_in_time: number; } // 可删除无用的PostTriviaScoreDto
修改trivia.controller.ts的参数配置:
import { Controller, Post, Body, ParseArrayPipe } from '@nestjs/common'; import { LoggerService } from '../logger/logger.service'; import { TriviaService } from './trivia.service'; import { TriviaScore } from './dto/post-trivia-score.dto'; // 导入TriviaScore @Controller('trivia') export class TriviaController { constructor(private readonly logger: LoggerService, private readonly triviaService: TriviaService) {} @Post('score') // items改为TriviaScore,参数类型改为数组 postTriviaScore(@Body(new ParseArrayPipe({ items: TriviaScore })) postTriviaScoreDto: TriviaScore[]) { this.logger.info('Trivia Controller : postTriviaScore : start'); // 修复未定义变量错误 return this.triviaService.postTriviaScore(postTriviaScoreDto); } }
修改trivia.service.ts的参数类型:
import { LoggerService } from '../logger/logger.service'; import { TriviaScore } from './dto/post-trivia-score.dto'; @Injectable() export class TriviaService { constructor( private readonly logger: LoggerService, ) {} // 参数类型改为TriviaScore数组 postTriviaScore(allPlayersTriviaScore: TriviaScore[]) { this.logger.info('Trivia Service : postTriviaScore : start'); console.log('allPlayersScore ', allPlayersTriviaScore); } }
方向二:保留当前PostTriviaScoreDto结构
修改请求的JSON结构,在外层包裹items字段:
{ "items": [ { "question_id": "088f1344-061e-4bcc-966f-512775f1f082", "selected_option_id": "72305e08-fedd-49b1-adb9-1dd92c88f4db", "answered_in_time": 2 } ] }
同时删除ParseArrayPipe配置,直接用DTO接收即可:
// controller修改 @Post('score') postTriviaScore(@Body() postTriviaScoreDto: PostTriviaScoreDto) { // 逻辑不变 }
同时给answered_in_time加上@IsNumber()校验即可。
内容的提问来源于stack exchange,提问作者Wasim
相关产品推荐
相关产品推荐

