You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS验证响应未显示属性名问题求助

在NestJS中给验证响应添加字段名

方法一:通过ValidationPipe的exceptionFactory直接格式化(简单快捷)

这种方法无需额外编写过滤器,直接在ValidationPipe配置中自定义异常内容,适合不需要自定义字段显示名的场景。

修改全局ValidationPipe配置,添加exceptionFactory选项:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe, UnprocessableEntityException } from '@nestjs/common';
import { ValidationError } from 'class-validator';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(
    new ValidationPipe({
      errorHttpStatusCode: 422,
      forbidUnknownValues: true,
      transform: true,
      whitelist: true,
      validationError: {
        target: true, // 开启以获取字段名
        value: false, // 可选,关闭则不返回字段值
      },
      // 自定义异常响应格式
      exceptionFactory: (validationErrors: ValidationError[] = []) => {
        const formattedMessages = validationErrors.map(error => {
          // 获取字段对应的错误消息(取第一个约束错误)
          const errorMsg = Object.values(error.constraints)[0];
          // 拼接字段名和消息
          return `${error.property}: ${errorMsg}`;
        });
        return new UnprocessableEntityException({
          statusCode: 422,
          message: formattedMessages,
          error: 'Unprocessable Entity',
        });
      },
    }),
  );

  await app.listen(3000);
}
bootstrap();

配置后,你会收到类似这样的响应:

{
  "statusCode": 422,
  "message": [
    "username: Uesrname is required",
    "firstName: First Name is required"
  ],
  "error": "Unprocessable Entity"
}

方法二:自定义全局异常过滤器(支持自定义字段显示名)

如果需要将字段名替换为更友好的名称(比如把firstName显示为First Name),可以用自定义异常过滤器结合元数据实现。

步骤1:创建字段名装饰器

定义一个装饰器,用来给DTO的字段标记友好显示名:

// src/common/decorators/field-name.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const FieldName = (name: string) => SetMetadata('fieldName', name);

步骤2:在DTO中使用装饰器

在验证DTO里,给字段加上@FieldName装饰器:

// src/user/dto/create-user.dto.ts
import { IsNotEmpty } from 'class-validator';
import { FieldName } from '../../common/decorators/field-name.decorator';

export class CreateUserDto {
  @FieldName('Username')
  @IsNotEmpty({ message: 'is required' })
  username: string;

  @FieldName('First Name')
  @IsNotEmpty({ message: 'is required' })
  firstName: string;
}

步骤3:创建全局异常过滤器

编写过滤器捕获验证异常,读取元数据并格式化响应:

// src/common/filters/validation-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, UnprocessableEntityException } from '@nestjs/common';
import { ValidationError } from 'class-validator';
import { Reflector } from '@nestjs/core';
import { Response } from 'express';

@Catch(UnprocessableEntityException)
export class ValidationExceptionFilter implements ExceptionFilter {
  constructor(private reflector: Reflector) {}

  catch(exception: UnprocessableEntityException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();
    const errorData = exception.getResponse() as { message: ValidationError[] | string[] };

    // 处理验证错误数组
    if (Array.isArray(errorData.message) && errorData.message[0] instanceof ValidationError) {
      const formattedMessages = (errorData.message as ValidationError[]).map(error => {
        // 读取自定义字段名,没有则用属性名
        const displayName = this.reflector.get<string>('fieldName', error.target.constructor.prototype[error.property]) || error.property;
        const errorMsg = Object.values(error.constraints)[0];
        return `${displayName} ${errorMsg}`;
      });

      response.status(status).json({
        statusCode: status,
        message: formattedMessages,
        error: 'Unprocessable Entity',
      });
    } else {
      // 非验证错误按原响应返回
      response.status(status).json(exception.getResponse());
    }
  }
}

步骤4:注册过滤器和ValidationPipe

在main.ts中注册全局过滤器和ValidationPipe:

import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { ValidationExceptionFilter } from './common/filters/validation-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const reflector = app.get(Reflector);

  app.useGlobalPipes(
    new ValidationPipe({
      errorHttpStatusCode: 422,
      forbidUnknownValues: true,
      transform: true,
      whitelist: true,
      validationError: {
        target: true, // 必须开启才能读取DTO的元数据
        value: false,
      },
    }),
  );

  // 注册全局异常过滤器
  app.useGlobalFilters(new ValidationExceptionFilter(reflector));

  await app.listen(3000);
}
bootstrap();

配置完成后,你会收到符合预期的响应:

{
  "statusCode": 422,
  "message": [
    "Username is required",
    "First Name is required"
  ],
  "error": "Unprocessable Entity"
}

内容的提问来源于stack exchange,提问作者Hasibur Rahman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 23:20:27