Запретить заданное значение c для DTO в Nestjs - PullRequest
3 голосов
/ 03 марта 2020

Мое перечисление "AppState" имеет следующие возможные значения перечисления:

export enum AppState {
  SUCCESS,
  ERROR,
  RUNNING
}

У меня есть UpdateAppStateDTO с appState, которое должно принимать все значения перечисления, кроме 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);
  }

Если запрос имеет пустое тело или недопустимое значение перечисления, например "foobar" для appState I получаю 400, что нормально.

Проблема в том, что когда я отправляю "RUNNING", я все равно получаю 200 вместо 400.

Как я могу предотвратить такое поведение?

Ответы [ 2 ]

0 голосов
/ 05 марта 2020

Проблема в том, что когда я отправляю "RUNNING", я все равно получаю 200 вместо 400.

кажется, что вы используете строку (!) "RUNNING "как значение в полезной нагрузке вашего запроса как таковое:

{ appState: "RUNNING" }

В этом случае IsEnum и NotEquals оба считают полезную нагрузку действительной.

Почему это так?

Прежде всего цифра c перечисления обратно отображаются с помощью машинописи , поэтому ваше перечисление внутренне (как javascript объект) представляется следующим образом:

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

Теперь класс-валидатор isEnum() кодируется следующим образом: :

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, который закодирован как таковой ...

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

будет сравнивать строку 'RUNNING' с AppState.RUNNING (что равно 2 ) и также пришли к выводу, что это действительно с 'RUNNING' != 2.

Итак, у вас есть, почему полезная нагрузка { appState: "RUNNING" } приведет к 200 вместо кода состояния 400.

Как я могу предотвратить это поведение?

Значение enum AppState.RUNNING равно 2, поэтому, когда вы делаете запрос, вы должны использовать значение цифра c 2 в вашем полезная нагрузка:

{ appState: 2 }

В приведенном выше случае валидатор NotEquals класса-валидатора затем корректно отклонит запрос с ответом, содержащим:

"constraints": {
    "notEquals": "appState should not be equal to 2"
}
0 голосов
/ 05 марта 2020

Я предполагаю, что вы отправляете в строке '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, позже будет по-прежнему числовым значением c вместо строки.

Вы можете поиграть с этим стеком Я сделал для этого, чтобы увидеть какой-нибудь работающий код.

...