禁止 Nestjs 中 DTO 的特定枚举值

Posted

技术标签:

【中文标题】禁止 Nestjs 中 DTO 的特定枚举值【英文标题】:Forbid specific enum value for DTO in Nestjs 【发布时间】:2020-06-15 17:46:58 【问题描述】:

我的“AppState”枚举具有以下可能的枚举值:

export enum AppState 
  SUCCESS,
  ERROR,
  RUNNING

我有一个带有appStateUpdateAppStateDTO,它应该接受除 RUNNING 之外的所有枚举值。

export class UpdateAppStateDTO 
  @IsEnum(AppState)
  @NotEquals(AppState.RUNNING) // Doesn't work properly
  public appState: AppState;

对于路线我有这个例子

  @Patch()
  public setState(@Body()  appState : UpdateAppStateDTO): void 
    console.log(appState);
  

如果请求的正文为空或appState 的“foobar”等无效枚举值,我会得到 400,这很好。

问题是当我发送“RUNNING”时,我仍然得到 200 而不是 400。

如何防止这种行为?

【问题讨论】:

【参考方案1】:

我假设您发送的是字符串'RUNNING',并且您正在尝试确保那是未使用的,对吗?根据您目前所拥有的,您的枚举映射到这些值:

export enum AppState 
  SUCCESS = 0,
  ERROR = 1,
  RUNNING = 2

因此,如果您发送字符串'RUNNING',验证器会检查RUNNING !== 2,这实际上是true,从而导致验证成功。 @IsEnum() 装饰器检查在枚举的有效键中发送的值,因此在 'RUNNING' 中发送通过了该检查,因此您在那里没有收到某种错误。

解决此问题的最详细方法是将您的枚举设置为 string enum,如下所示:

export enum AppState 
  SUCCESS = 'SUCCESS',
  ERROR = 'ERROR',
  RUNNING = 'RUNNING'

这将使每个AppState 值映射到其对应的字符串,但这确实会导致必须输入大量声明并可能导致重复代码。

另一种管理方法是将 @NotEquals() 枚举设置为枚举值提供的键,如下所示:

export class UpdateAppStateDTO 
  @IsEnum(AppState)
  @NotEquals(AppState[AppState.RUNNING])
  public appState: AppState;

但请记住,使用这种方法,当您稍后查看appState 时,它仍然是一个数值而不是一个字符串。

您可以使用this stackblitz 我为此制作的以查看一些正在运行的代码。

【讨论】:

【参考方案2】:

问题是当我发送“RUNNING”时,我仍然得到 200 而不是 400。

您似乎在请求有效负载中使用字符串(!)“RUNNING”作为值:

 appState: "RUNNING" 

在这种情况下,IsEnumNotEquals 都认为有效载荷是有效的。

这是为什么呢?

首先numeric enums are reverse mapped by typescript 所以你的枚举在内部(作为javascript对象)表示如下:


  '0': 'SUCCESS',
  '1': 'ERROR',
  '2': 'RUNNING',
  'SUCCESS': 0,
  'ERROR': 1,
  'RUNNING': 2

现在类验证器的isEnum()is coded as follows:

isEnum(value: unknown, entity: any): boolean 
    const enumValues = Object.keys(entity)
        .map(k => entity[k]);
    return enumValues.indexOf(value) >= 0;

由于枚举是反向映射的,isEnum('RUNNNING', AppState) 将返回 true。

同时NotEquals,其中is coded as such...

notEquals(value: unknown, comparison: unknown): boolean 
    return value !== comparison;

将字符串“RUNNING”与AppState.RUNNING(等同于2)进行比较,并得出结论,自'RUNNING' != 2以来这是有效的。

所以你知道为什么负载 appState: "RUNNING" 会导致 200 而不是 400 状态代码。

如何防止这种行为?

枚举值AppState.RUNNING 等同于2,因此当您发出请求时,您应该在有效负载中使用2 的数值:

 appState: 2 

在上述情况下,类验证器的NotEquals 验证器将正确拒绝请求,响应包含:

"constraints": 
    "notEquals": "appState should not be equal to 2"

【讨论】:

以上是关于禁止 Nestjs 中 DTO 的特定枚举值的主要内容,如果未能解决你的问题,请参考以下文章

禁止数组中的重复项

怎么禁止浏览某个特定网站

如何在 NestJS 中编写嵌套 DTO

关闭默认共享,禁止ipc$空连接

NestJs:为啥我们不使用 DTO 来替换所有接口?

NestJs - DTO 和实体