Я работаю над небольшим проектом 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 не отображает необходимый параметр.
Скриншот Swagger, не отображающего необходимый параметр
Поэтому я последовал этому посту о переполнении стека, чтобы реализовать документацию в моем собственном декораторе.
Вот мой новый пользовательский декоратор:
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:
Скриншот Swagger, отображающий uuid вместо обязательного параметра
Но я хочу отображать eventId или имя параметра в общем виде (например, ticketId где-то еще в другом контроллере).
Я пытался получить что-то из свойств target и key, но ничего не нашел.
Я ничего не нашел ни в Интернете, ни в
Знаете ли вы, как я могу решить свою проблему?






Наконец я нашел ответ.
Мы можем решить эту проблему, инкапсулировав функцию 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.