升级NestJS 10至11后JSON对象解析异常问题排查与修复
我有一个NestJS应用,包含如下POST端点:
@Post() @HttpCode(HttpStatus.CREATED) public async create(@Body() model: SerializedModel, @Req() req): Promise<SomeDto> { const sessionId = await this.modelStorage.add(await this.parseRequest(model, req)); return {id: sessionId}; }
SerializedModel是带有自定义属性的JSON对象:
export const RootObjectMidPropertyKey = "rootObjectMID"; export class SerializedModel { [key: string]: any; public [RootObjectMidPropertyKey]: string; }
SerializedModel的示例数据:
{ "rootObjectMID": "W39MCbKB-session", "W39MCbKB-session": { "_mid": "W39MCbKB-session", "code": "W39MCbKB", "locale": "en-IE", "urlParameters": "?locale=en-IE", "unstructuredState": "unstructured-45353-3-34538134" } }
在遍历SerializedModel实例的entries时出现异常:
Object.entries(serializedModel).forEach(([modelKey, modelValue]) => { stringifiedModel[modelKey] = JSON.stringify(modelValue); });
打印结果显示键值对被逐个字符拆分:
modelKey: 0 modelValue: { modelKey: 1 modelValue: modelKey: 2 modelValue: modelKey: 3 modelValue: modelKey: 4 modelValue: modelKey: 5 modelValue: " modelKey: 6 modelValue: r modelKey: 7 modelValue: o modelKey: 8 modelValue: o modelKey: 9 modelValue: t modelKey: 10 modelValue: O
而非预期的正常键值对:
modelKey: rootObjectMID modelValue: W39MCbKB-session
代码逻辑未改动,请问NestJS新版本在JSON处理上有何默认行为变更?该如何修复?
NestJS从v9升级到v10后,默认的JSON解析器从body-parser切换为@nestjs/platform-express内置的基于express.json()的解析器,且默认配置中开启了strict: true。这个配置会导致当传入的JSON无法被正确序列化为指定的类(SerializedModel)时,解析器会将其转换为字符串而非JSON对象。
你的SerializedModel使用了索引签名,但NestJS的ClassValidator/ClassTransformer在处理这类动态属性时,若未正确配置,会导致解析后的实例并非预期的JSON对象,而是被当作字符串处理,最终Object.entries遍历的是字符串的字符序列。
有两种可行的修复方式:
方式一:修改JSON解析器配置
在main.ts中显式配置express的JSON解析器,关闭strict模式:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { json } from 'express'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 覆盖默认JSON解析配置,关闭strict模式 app.use(json({ strict: false })); await app.listen(3000); } bootstrap();
方式二:调整SerializedModel的转换配置
使用class-transformer的装饰器,明确允许动态属性,并确保解析时正确转换:
import { Transform } from 'class-transformer'; export const RootObjectMidPropertyKey = "rootObjectMID"; export class SerializedModel { [key: string]: any; public [RootObjectMidPropertyKey]: string; // 自定义转换逻辑,确保动态属性被正确解析 @Transform(({ value, obj }) => { return obj; }) get dynamicProperties(): Record<string, any> { const result = { ...this }; delete result[RootObjectMidPropertyKey]; return result; } }
同时在main.ts中确保开启了enableImplicitConversion:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ enableImplicitConversion: true, transform: true, })); await app.listen(3000); } bootstrap();
验证修复
修改后,Object.entries(serializedModel)将正确遍历JSON对象的键值对,而非字符串的字符。
内容的提问来源于stack exchange,提问作者irky

