foreword
When verifying parameters at the DTO layer of Nest, a parameter may have multiple types. How would you deal with this situation? This article will share the solution to this problem with you. Interested developers are welcome to read this article.
Scenario overview
When we are developing the interface, the client needs to pass in a field named text
, which may be string
type or Array<Object>
type (in TS we This relationship is called a union type ), the class-validator
library provides relevant verification annotations, then write them together to complete the relevant verification, as shown below:
export class AppDto {
@ApiProperty({ example: "2022年4月20日修改", description: "备注" })
@IsString()
@IsArray()
@ValidateNested({ each: true })
@Type(() => TextObjDto)
public text!: string | Array<TextObjType>;
}
The code for TextObjDto looks like this:
export class TextObjDto {
@ApiProperty({ example: "修复了一些bug", description: "内容" })
@IsString()
content!: string;
@ApiProperty({ example: "2022-04-20 07:52", description: "创建时间" })
@IsString()
createTime?: string;
@ApiProperty({ example: true, description: "是否为新功能标识" })
@IsBoolean()
mark?: boolean;
}
Start the project and use postman to test it and find that it is not easy to use. The data of the array type is required to be of the string type, and the data of the string type is required to be of the array type.
Note: Object validation of nested types needs to use @ValidateNested and @Type annotations, @Type accepts a callback function, and the function needs to return a dto class declared with class.
solution
After some help, I went through the documentation of class-validator
and found that there is no ready-made solution. Then, you can only get the parameters yourself to do custom verification.
In the class-transformer
library, the Transform
method is provided, which accepts a callback function as a parameter, and the callback function provides a TransformFnParams
type parameter. The value field is the parameter passed by the client, and we only need to verify it.
Next, let's look at the implementation code, as follows:
export class AppDto {
@ApiProperty({ example: "2022年4月20日修改", description: "备注" })
@IsOptional()
@Transform(({ value }) => checkTitleKey(value))
public text!: string | Array<TextObjType>;
}
In the above code, we have a verification function named checkTitleKey
, because we need to verify by ourselves, we need to copy the type verification of TS by ourselves. The implementation code is as follows:
- If the verification is passed, return
value
parameter directly. - If the validation is not thrown by directly using the nest built-in exception
export function checkTitleKey(
value: string | number | Array<TextObjType> | undefined | null
): any {
if (typeof value === "string") {
// 不做更改,直接返回
return value;
} else if (value instanceof Array) {
// 不能为空数组
if (value.length <= 0) {
throw new BadRequestException(
"property text cannot be an empty array",
"Bad Request"
);
}
for (let i = 0; i < value.length; i++) {
// 校验数组中的对象字段
const objKeys = Object.keys(value[i]);
if (objKeys.length <= 0) {
throw new BadRequestException(
"property text contains empty objects",
"Bad Request"
);
}
// 必须包含content字段
if (!objKeys.includes("content")) {
throw new BadRequestException(
"property text objects in the array must contain 'content'",
"Bad Request"
);
}
// 对每个key进行校验
for (let j = 0; j < objKeys.length; j++) {
switch (objKeys[j]) {
case "content":
// content字段必须为string类型
if (typeof value[i].content !== "string") {
throw new BadRequestException(
"property text 'content' of the objects in the array must be of type string",
"Bad Request"
);
}
break;
case "duration":
if (typeof value[i].createTime !== "string") {
throw new BadRequestException(
"property text 'createTime' of the objects in the array must be of type number",
"Bad Request"
);
}
break;
case "delay":
if (typeof value[i].mark !== "boolean") {
throw new BadRequestException(
"property text 'mark' of the objects in the array must be of type number",
"Bad Request"
);
}
break;
default:
break;
}
}
}
return value;
} else {
throw new BadRequestException(
"text must be an array or string",
"Bad Request"
);
}
}
The declaration of TextObjType
also needs to be modified accordingly, as follows:
- All become optional parameters, and whether the parameters must be passed or not has been processed in the verification function
- All types become any
export type TextObjType = {
content?: any;
createTime?: any;
mark?: any;
};
Some developers may be confused. Isn't it a shameful act to use any in ts? I'll correct you, since it exists and naturally has usage scenarios. In my scenario, the type verification of all keys in the object is handled manually. If its type is defined here, a yellow warning will be reported in the verification function, so it is suitable for scenarios that require manual type verification. For example, using any is the most appropriate.
result check
Finally, we verify that it works correctly against the exception rules defined in the code, as follows:
# text字段为string类型
{
"id":"122211",
"title":"新的标题",
"text":"新替换的文本内容",
"name":"新的名字",
"config":"var config = {\"name\":\"aa\",\"age\":\"21\",\"title\":\"标题测试\"}"
}
>>> 接口调用成功
# text字段为Array类型所有key都存在
{
"id":"122211",
"title":"新的标题",
"text":[{"content":"新文本","createTime":"2022-04-20","mark":false}],
"name":"新的名字",
"config":"var config = {\"name\":\"aa\",\"age\":\"21\",\"title\":\"标题测试\"}"
}
>>> 接口调用成功
# text字段缺少content
{
"id":"122211",
"title":"新的标题",
"text":[{"createTime":"2022-04-20","mark":false}],
"name":"新的名字",
"config":"var config = {\"name\":\"aa\",\"age\":\"21\",\"title\":\"标题测试\"}"
}
>>> 接口报错400:property text objects in the array must contain 'content'
# text字段为number类型
{
"id":"122211",
"title":"新的标题",
"text":19,
"name":"新的名字",
"config":"var config = {\"name\":\"aa\",\"age\":\"21\",\"title\":\"标题测试\"}"
}
>>> 接口报错400:text must be an array or string
# text字段缺少createTime与mark
{
"id":"122211",
"title":"新的标题",
"text":[{"content":"新文本"}],
"name":"新的名字",
"config":"var config = {\"name\":\"aa\",\"age\":\"21\",\"title\":\"标题测试\"}"
}
>>> 接口调用成功
As shown in the figure below, we enumerate a screenshot of the error report when the text field is a number. The running result is as expected, and the problem at the beginning of the article is successfully solved🤗
sample code
For the full version of the code cited in the article, please move to:
write at the end
At this point, the article is shared.
I'm an amazing programmer , a front-end developer.
If you are interested in me, please visit my personal website to learn more.
- If there are any mistakes in the article, please correct them in the comment area. If this article helps you, please like and follow 😊
- This article was first published on the magical programmer's official account, and reprinting is prohibited without permission 💌
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。