禁止 Nestjs 中 DTO 的特定枚举值
Forbid specific enum value for DTO in Nestjs
我的“AppState”枚举有以下可能的枚举值:
export enum AppState {
SUCCESS,
ERROR,
RUNNING
}
我有一个 UpdateAppStateDTO 和一个 appState
应该接受除 运行 之外的每个枚举值。
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,这很好。
问题是,当我发送“运行”时,我仍然收到 200 而不是 400。
如何防止这种行为?
我假设您发送的是字符串 '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 我为此做了一些 运行 代码。
The problem is that when I send "RUNNING" I'm still getting a 200 instead of a 400.
您似乎在请求负载中使用字符串 (!)“运行”作为值:
{ appState: "RUNNING" }
在这种情况下,IsEnum
和NotEquals
都认为有效载荷有效。
这是为什么?
首先 numeric enums are reverse mapped by typescript 所以你的枚举在内部(作为 javascript 对象)表示如下:
{
'0': 'SUCCESS',
'1': 'ERROR',
'2': 'RUNNING',
'SUCCESS': 0,
'ERROR': 1,
'RUNNING': 2
}
现在 class-验证者的 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)
将 return 为真。
同时NotEquals
,其中is coded as such...
notEquals(value: unknown, comparison: unknown): boolean {
return value !== comparison;
}
将字符串 'RUNNING' 与 AppState.RUNNING
(等于 2
)进行比较,并得出结论认为这是有效的,因为 'RUNNING' != 2
.
所以你明白了为什么负载 { appState: "RUNNING" }
会导致 200 而不是 400 状态代码。
How can I prevent this behaviour?
枚举值 AppState.RUNNING
等于 2
所以当你发出请求时,你应该在你的有效负载中使用 2
的数值:
{ appState: 2 }
在上述情况下,class-验证器的 NotEquals
验证器将正确拒绝请求,响应包含:
"constraints": {
"notEquals": "appState should not be equal to 2"
}
我的“AppState”枚举有以下可能的枚举值:
export enum AppState {
SUCCESS,
ERROR,
RUNNING
}
我有一个 UpdateAppStateDTO 和一个 appState
应该接受除 运行 之外的每个枚举值。
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,这很好。
问题是,当我发送“运行”时,我仍然收到 200 而不是 400。
如何防止这种行为?
我假设您发送的是字符串 '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 我为此做了一些 运行 代码。
The problem is that when I send "RUNNING" I'm still getting a 200 instead of a 400.
您似乎在请求负载中使用字符串 (!)“运行”作为值:
{ appState: "RUNNING" }
在这种情况下,IsEnum
和NotEquals
都认为有效载荷有效。
这是为什么?
首先 numeric enums are reverse mapped by typescript 所以你的枚举在内部(作为 javascript 对象)表示如下:
{
'0': 'SUCCESS',
'1': 'ERROR',
'2': 'RUNNING',
'SUCCESS': 0,
'ERROR': 1,
'RUNNING': 2
}
现在 class-验证者的 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)
将 return 为真。
同时NotEquals
,其中is coded as such...
notEquals(value: unknown, comparison: unknown): boolean {
return value !== comparison;
}
将字符串 'RUNNING' 与 AppState.RUNNING
(等于 2
)进行比较,并得出结论认为这是有效的,因为 'RUNNING' != 2
.
所以你明白了为什么负载 { appState: "RUNNING" }
会导致 200 而不是 400 状态代码。
How can I prevent this behaviour?
枚举值 AppState.RUNNING
等于 2
所以当你发出请求时,你应该在你的有效负载中使用 2
的数值:
{ appState: 2 }
在上述情况下,class-验证器的 NotEquals
验证器将正确拒绝请求,响应包含:
"constraints": {
"notEquals": "appState should not be equal to 2"
}