NestJS中如何兼容处理请求体的camelCase转snake_case与原生snake_case请求?
NestJS中如何兼容处理请求体的camelCase转snake_case与原生snake_case请求?
嗨,这个需求我之前在项目里也踩过坑,你现在用@Transform结合@Expose的思路其实是可行的,但每个字段都写一遍确实太繁琐了,我给你分享两个更优雅的方案,帮你减少重复代码:
方案一:封装自定义装饰器(字段级优化)
我们可以把你现在写的重复逻辑封装成一个可复用的装饰器,这样每个字段只需要加一行注解就行,代码会干净很多:
首先创建一个自定义装饰器:
import { Transform } from 'class-transformer'; // 自动兼容camelCase和snake_case的装饰器 export const AllowCamelOrSnake = () => { return Transform(({ obj, key }) => { // 把snake_case的字段名转成对应的camelCase const camelCaseKey = key.replace(/_([a-z])/g, (match) => match[1].toUpperCase()); // 优先取camelCase的值,没有的话用原snake_case的值 return obj[camelCaseKey] ?? obj[key]; }); };
然后在你的DTO里直接用这个装饰器:
@ApiProperty({ description: "name" }) @IsString() @IsNotEmpty() @AllowCamelOrSnake() first_name: string;
这样不管请求里传的是firstName还是first_name,都能正确映射到DTO的first_name字段,而且代码简洁很多,不用每个字段都写重复的逻辑。
方案二:全局管道处理(全局统一兼容)
如果你的项目里很多DTO都需要这个功能,那写全局管道会更高效,一次性解决所有请求的兼容问题,不用在每个DTO的字段上写装饰器:
先写一个全局管道:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common'; import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; @Injectable() export class CamelSnakeCompatiblePipe implements PipeTransform { async transform(value: any, { metatype }: ArgumentMetadata) { // 如果不是DTO类型,直接返回原数据 if (!metatype || !this.shouldValidate(metatype)) { return value; } // 把请求体里的camelCase字段转成snake_case,同时保留原有的snake_case字段 const normalizedBody = this.normalizeCase(value); // 转换成DTO实例并做验证 const dtoInstance = plainToInstance(metatype, normalizedBody); const validationErrors = await validate(dtoInstance); if (validationErrors.length > 0) { throw new BadRequestException('请求参数验证失败'); } return dtoInstance; } // 递归处理对象、数组的字段名转换 private normalizeCase(data: any): any { if (typeof data !== 'object' || data === null) { return data; } if (Array.isArray(data)) { return data.map(item => this.normalizeCase(item)); } return Object.entries(data).reduce((acc, [key, val]) => { // 把camelCase转成snake_case const snakeCaseKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); // 避免覆盖原有的snake_case字段 if (!acc.hasOwnProperty(snakeCaseKey)) { acc[snakeCaseKey] = this.normalizeCase(val); } return acc; }, { ...data }); // 先复制原数据,保留所有原生snake_case字段 } // 判断是否需要验证(只针对自定义的DTO类) private shouldValidate(metatype: Function): boolean { const basicTypes = [String, Boolean, Number, Array, Object]; return !basicTypes.includes(metatype); } }
然后在main.ts里全局注册这个管道:
async function bootstrap() { const app = await NestFactory.create(AppModule); // 全局注册兼容管道 app.useGlobalPipes(new CamelSnakeCompatiblePipe()); await app.listen(3000); } bootstrap();
这样配置后,所有的请求体不管是用camelCase还是snake_case传参,都会被自动转换成snake_case格式和DTO字段匹配,完全不用在DTO里写额外的装饰器,非常省心。
小提示
- 如果你用的是NestJS的
ValidationPipe,可以把上面的逻辑和ValidationPipe结合起来,不用重复写验证逻辑 - 全局管道要注意处理嵌套对象和数组的情况,上面的
normalizeCase方法已经做了递归处理,能覆盖大部分场景
内容来源于stack exchange
相关产品推荐
相关产品推荐

