有没有办法将默认值设置为缺少/可选的 JSON 属性?

Posted

技术标签:

【中文标题】有没有办法将默认值设置为缺少/可选的 JSON 属性?【英文标题】:Is there a way to set default value to missing/optional JSON attribute? 【发布时间】:2019-05-30 08:50:24 【问题描述】:

我使用 NodeJs/NestJs 来构建一个 RESTful 服务。我创建了一些对象来匹配请求 JSON。在这些对象中有一些可选属性,但如果客户端不通过 JSON 发送它们,我想为它们设置默认值。

实现目标的最佳方法是什么?

这是我与 JSON 匹配的 DTO。

import  IsDefined, IsNumber, Min  from 'class-validator';
import  ApiModelProperty, ApiModelPropertyOptional  from '@nestjs/swagger';

export class RequestDto 
    @IsDefined()
    @IsNumber()
    @Min(0)
    @ApiModelProperty(description: 'The current age.')
    public CurrentAge: number;

    @ApiModelPropertyOptional(description: 'The existing saving amount.')
    public ExistingSavingAmount: number = 0;

这是我的 NestJs 控制器

import  Controller, Post, Body, Param  from '@nestjs/common';
import  RequestDto  from './Dto/Request.Dto';
import  ApiResponse, ApiOperation  from '@nestjs/swagger';

@Controller('mycontroller')
export class MyController 
    @Post('MyEndPoint')
    @ApiOperation( title: 'Do something' )
    @ApiResponse( status: 201, description: 'Something is done' )
    public doSomething(@Body() request: RequestDto) 
        // do more jobs
    

我启动服务,并将以下 JSON 发布到我的终点


    "CurrentAge": 40,

在我的控制器中,我看到 ExistingSavingAmount 是空白的,而不是值为 0。但如果我直接实例化 RequestDto,我可以看到 ExistingSavingAmount 的值为 0。

【问题讨论】:

您能否以您现有的端点之一为例,甚至可以使用示例 JSON 向我们展示您的意思? @Paul 我已经用代码示例更新了我的帖子。 【参考方案1】:

好的,如果没有来自 OP 的代码示例,此响应的保真度可能需要改进。也就是说,执行此操作的“嵌套”方式是通过TransformPipe。

他们给出的典型例子是 ParseIntPipe:

import  Injectable, BadRequestException from '@nestjs/common';

@Injectable()
export class ParseIntPipe 
  transform(value, metadata) 
    const val = parseInt(value, 10);
    if (isNaN(val)) 
      throw new BadRequestException('Validation failed');
    
    return val;
  

在不知道你的默认值是什么样子的情况下,我会假设它就像一个产品,你想默认一些东西并将一些东西作为一个空字符串:

import  Injectable, BadRequestException from '@nestjs/common';

// we will assume you have your own validation for the non-optional bits
const optionalDefaults = 
   description: '',
   category: 'Miscelleneous'


@Injectable()
export class ProductDefaultsPipe 
  transform(value, metadata) 
    const val = Object.assign(optionalDefaults, value);
    return val;
  

现在,您可能正在使用提供模式和模型定义的东西(例如 Joi 或 Mongoose)。如果您是,那么我建议在该架构中设置所有默认值和验证,然后在您的 TransformPipe 中应用该架构,而不是编写太多自定义代码。例如,如果你有一个 ProductSchema,这对你有用:

@Injectable()
export class ProductDefaultsPipe 
  async transform(value, metadata) 
    const val = new Product(value);
    const isValid = await val.validate();
    if (!isValid) 
       throw new BadRequestException('Validation failed');
    
    return val;
  

【讨论】:

【参考方案2】:

只有当RequestDto 实际实例化为一个类时,您的默认值才会适用。由于您已经在使用 class-validator 进行验证,因此您可以使用 classTransformer.plainToClass() 来实例化该类。

如果您使用内置的ValidationPipe,您可以使用 transform: true 选项自动实例化您的RequestDto 类:

@UsePipes(new ValidationPipe( transform: true ))
@Post('MyEndPoint')
public doSomething(@Body() request: RequestDto) 

或作为全局管道:

async function bootstrap() 
  const app = await NestFactory.create(ApplicationModule);
  app.useGlobalPipes(new ValidationPipe( transform: true ));
  await app.listen(3000);

bootstrap();

【讨论】:

非常感谢您的意见。进一步的问题,不使用classTransformer.plainToClass()@UsePipes(new ValidationPipe( transform: true )),DTO类没有实例化,那么它来自哪里?由于我来自 C# 背景,在这种情况下,对象是从 JSON 反序列化的,因此它由序列化程序实例化。 javascript/TypeScript 在这种情况下有很大不同? 如果没有类转换器,它将不会从 JSON 反序列化,而是保持一个普通的 Javascript 对象。由于这发生在运行时,因此没有类型检查。事实上,在运行时你不会有 RequestDto 对象,而是会有一个普通对象(希望没有人检查)符合你的 RequestDto 接口。

以上是关于有没有办法将默认值设置为缺少/可选的 JSON 属性?的主要内容,如果未能解决你的问题,请参考以下文章

Jackson 2.11.4:为缺少的 JSON 字段设置默认值

如何为注释类型编码可选的默认注释值

jquery ajax如何判断正在进行

将 NSDate 的默认值设置为今天

猫鼬返回默认值而不是空值

Swift之Optional 可选值