如何在NestJS中完全自定义Param装饰器以在SwaggerUI中显示正确的属性

问题描述 投票:0回答:1

我正在开发一个 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 未显示所需参数的屏幕截图

所以我按照 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
:

Swagger 显示 uuid 而不是所需参数的屏幕截图

但我想以通用方式显示

eventId
或参数名称(例如
ticketId
在另一个控制器的其他地方)。

我试图从

target
key
属性中获取一些东西,但我没有找到任何东西。 我在 Internet 上和 ChatGPT 上都没有找到任何内容,并且在我尝试自定义 Swagger 文档的
data
方法的第二个参数中无法访问
createParamDecorator()
属性。

您知道如何解决我的问题吗?

typescript nestjs swagger swagger-ui nestjs-swagger
1个回答
0
投票

我终于找到答案了。

我们可以通过将

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中动态显示我们自定义装饰器的字符串参数。

© www.soinside.com 2019 - 2024. All rights reserved.