我正在开发一个 NestJS 小项目来创建和管理活动和门票。
因此,我在控制器的请求参数中使用 UUID,如下所示来获取、更新和删除数据库中的实体:
@Get(':eventId')
async findOne(
@UserId() userId: string,
@Param('eventId') eventId: string,
): Promise<EventEntity> {
return this.eventService.findOne(userId, eventId);
}
到目前为止,没有任何问题。
然后,我实现了一个自定义装饰器来验证 UUID 格式。这是代码片段:
export const IsUUIDParam = createParamDecorator(
(data: string, ctx: ExecutionContext): string => {
const request = ctx.switchToHttp().getRequest();
const uuid: string = request.params[data];
if (!uuid) {
return uuid;
}
// This regex checks if the string is a valid UUIDv4
const cmp: RegExpMatchArray = uuid.match(
'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
);
if (cmp === null) {
throw new BadRequestException(`Invalid ${data} format`);
}
return uuid;
},
);
我在控制器中使用了这样的新装饰器:
@Get(':eventId')
async findOne(
@UserId() userId: string,
@IsUUIDParam('eventId') eventId: string,
): Promise<EventEntity> {
return this.eventService.findOne(userId, eventId);
}
自定义装饰器运行良好,但目前 Swagger 未显示所需的参数。
所以我按照 this Stack Overflow post 在我的自定义装饰器上实现文档。
这是我的新定制装饰器:
export const IsUUIDParam = createParamDecorator(
(data: string, ctx: ExecutionContext): string => {
const request = ctx.switchToHttp().getRequest();
const uuid: string = request.params[data];
if (!uuid) {
return uuid;
}
// This regex checks if the string is a valid UUIDv4
const cmp: RegExpMatchArray = uuid.match(
'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
);
if (cmp === null) {
throw new BadRequestException(`Invalid ${data} format`);
}
return uuid;
},
[
(target, key): void => {
// The code below define the Swagger documentation for the custom decorator
const explicit =
Reflect.getMetadata(DECORATORS.API_PARAMETERS, target[key]) ?? [];
Reflect.defineMetadata(
DECORATORS.API_PARAMETERS,
[
...explicit,
{
in: 'path',
name: 'uuid',
required: true,
type: 'string',
},
],
target[key],
);
},
],
);
但是现在,Swagger 文档只显示
uuid
:
但我想以通用方式显示
eventId
或参数名称(例如 ticketId
在另一个控制器的其他地方)。
我试图从
target
和 key
属性中获取一些东西,但我没有找到任何东西。
我在 Internet 上和 ChatGPT 上都没有找到任何内容,并且在我尝试自定义 Swagger 文档的 data
方法的第二个参数中无法访问 createParamDecorator()
属性。
您知道如何解决我的问题吗?
我终于找到答案了。
我们可以通过将
createParamDecorator()
函数封装在带有字符串参数(此处为 data
参数)的箭头函数中来解决此问题。
export const IsUUIDParam = (data: string) => // new arrow function that takes a string
createParamDecorator(
// the object _ allow us to declare to our IDE that the parameter won't be used, and so it doesn't display a warning message
(_: string, ctx: ExecutionContext): string => {
const request = ctx.switchToHttp().getRequest();
const uuid: string = request.params[data];
if (!uuid) {
return uuid;
}
// This regex checks if the string is a valid UUIDv4
const cmp: RegExpMatchArray = uuid.match(
'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
);
if (cmp === null) {
throw new BadRequestException(`Invalid ${data} format`);
}
return uuid;
},
[
(target, key): void => {
// The code below define the Swagger documentation for the custom decorator
const explicit =
Reflect.getMetadata(DECORATORS.API_PARAMETERS, target[key]) ?? [];
Reflect.defineMetadata(
DECORATORS.API_PARAMETERS,
[
...explicit,
{
in: 'path',
// use the new data object here
name: data,
required: true,
type: 'string',
},
],
target[key],
);
},
],
// Do not forget to add the parenthesis at the end to execute the arrow function
)();
因此,
data
对象(包含UUIDv4字符串)可以在箭头函数中的任何位置访问,因此在createParamDecorator()
函数的第二部分也是如此。
我们可以通过下划线 (createParamDecorator()
) 更改 _
函数的第一个参数,以避免 IDE 中出现警告消息,因为我们不再使用此参数。
然后我们可以用 name
更新装饰器中的 data
属性来显示给定的名称(以动态方式)。
最后在箭头函数末尾添加括号即可执行(()
)
控制器中没有任何变化,我们仍然可以使用以下代码调用自定义装饰器:
@Get(':eventId')
async findOne(
@UserId() userId: string,
@IsUUIDParam('eventId') eventId: string,
): Promise<EventEntity> {
return this.eventService.findOne(userId, eventId);
}
这是 Swagger 结果的屏幕截图:
Swagger 结果的屏幕截图 - GET /event/:eventId
对于门票实体:
Swagger 结果的屏幕截图 - GET /ticket/:ticketId
注意:如果你想自定义更多的装饰器(例如添加自定义描述),可以在函数原型中的
data
后面添加一个参数,在控制器中调用该函数时给出第二个参数,最后使用新参数位于箭头函数中任意位置。
瞧!我们可以在Swagger中动态显示我们自定义装饰器的字符串参数。