跳转至

操作

在 OpenAPI 术语中,路径是 API 公开的端点(资源),如/users/reports/summary,操作是用于操作这些路径的 HTTP 方法,如GETPOSTDELETE

标签

要将一个控制器附加到一个特定的标签,可以使用@ApiTags(…tags)装饰器。

1
2
3
@ApiTags('cats')
@Controller('cats')
export class CatsController {}

要定义作为请求一部分的自定义头文件,请使用@ApiHeader()

1
2
3
4
5
6
@ApiHeader({
  name: 'X-MyHeader',
  description: 'Custom header',
})
@Controller('cats')
export class CatsController {}

响应

要定义一个自定义的 HTTP 响应,使用@ApiResponse()装饰器。

1
2
3
4
5
6
@Post()
@ApiResponse({ status: 201, description: 'The record has been successfully created.'})
@ApiResponse({ status: 403, description: 'Forbidden.'})
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

Nest 提供了一组简短的 API 响应 装饰器,它们继承自@ApiResponse装饰器:

  • @ApiOkResponse()
  • @ApiCreatedResponse()
  • @ApiAcceptedResponse()
  • @ApiNoContentResponse()
  • @ApiMovedPermanentlyResponse()
  • @ApiBadRequestResponse()
  • @ApiUnauthorizedResponse()
  • @ApiNotFoundResponse()
  • @ApiForbiddenResponse()
  • @ApiMethodNotAllowedResponse()
  • @ApiNotAcceptableResponse()
  • @ApiRequestTimeoutResponse()
  • @ApiConflictResponse()
  • @ApiTooManyRequestsResponse()
  • @ApiGoneResponse()
  • @ApiPayloadTooLargeResponse()
  • @ApiUnsupportedMediaTypeResponse()
  • @ApiUnprocessableEntityResponse()
  • @ApiInternalServerErrorResponse()
  • @ApiNotImplementedResponse()
  • @ApiBadGatewayResponse()
  • @ApiServiceUnavailableResponse()
  • @ApiGatewayTimeoutResponse()
  • @ApiDefaultResponse()
1
2
3
4
5
6
@Post()
@ApiCreatedResponse({ description: 'The record has been successfully created.'})
@ApiForbiddenResponse({ description: 'Forbidden.'})
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

为了指定请求的返回模型,我们必须创建一个类,并使用@ApiProperty()装饰器注释所有属性。

export class Cat {
  @ApiProperty()
  id: number;

  @ApiProperty()
  name: string;

  @ApiProperty()
  age: number;

  @ApiProperty()
  breed: string;
}

然后Cat模型可以与响应装饰器的type属性结合使用。

@ApiTags('cats')
@Controller('cats')
export class CatsController {
  @Post()
  @ApiCreatedResponse({
    description: 'The record has been successfully created.',
    type: Cat,
  })
  async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
    return this.catsService.create(createCatDto);
  }
}

让我们打开浏览器并验证生成的Cat模型:

文件上传

你可以使用@ApiBody装饰器和@ApiConsumes()来启用特定方法的文件上传。 下面是一个使用File Upload技术的完整示例:

1
2
3
4
5
6
7
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiBody({
  description: 'List of cats',
  type: FileUploadDto,
})
uploadFile(@UploadedFile() file) {}

其中FileUploadDto的定义如下:

1
2
3
4
class FileUploadDto {
  @ApiProperty({ type: 'string', format: 'binary' })
  file: any;
}

要处理多个文件上传,可以定义如下FilesUploadDto:

1
2
3
4
class FilesUploadDto {
  @ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } })
  files: any[];
}

扩展

要向请求添加 Extension,请使用@ApiExtension()装饰器。 扩展名必须以x-作为前缀。

@ApiExtension('x-foo', { hello: 'world' })

高级:通用ApiResponse

有了提供Raw Definitions的能力,我们可以为 Swagger UI 定义通用模式。 假设我们有以下 DTO:

export class PaginatedDto<TData> {
  @ApiProperty()
  total: number;

  @ApiProperty()
  limit: number;

  @ApiProperty()
  offset: number;

  results: TData[];
}

我们跳过修饰结果,因为我们稍后将提供它的原始定义。 现在,让我们定义另一个 DTO 并命名它,例如,CatDto,如下所示:

export class CatDto {
  @ApiProperty()
  name: string;

  @ApiProperty()
  age: number;

  @ApiProperty()
  breed: string;
}

有了这些,我们可以定义一个PaginatedDto<CatDto>响应,如下所示:

@ApiOkResponse({
  schema: {
    allOf: [
      { $ref: getSchemaPath(PaginatedDto) },
      {
        properties: {
          results: {
            type: 'array',
            items: { $ref: getSchemaPath(CatDto) },
          },
        },
      },
    ],
  },
})
async findAll(): Promise<PaginatedDto<CatDto>> {}

在本例中,我们指定响应的所有属性为PaginatedDto,而results属性的类型为Array<CatDto>

  • getSchemaPath()函数,返回给定模型 OpenAPI 规范文件中的 OpenAPI 架构路径。 — allOf是 OAS 3 提供的一个概念,用于覆盖各种与继承相关的用例。

最后,由于PaginatedDto没有被任何控制器直接引用,SwaggerModule还不能生成相应的模型定义。 在这种情况下,我们必须将它添加为Extra Model。 例如,我们可以在控制器层使用@ApiExtraModels()装饰器,如下所示:

1
2
3
@Controller('cats')
@ApiExtraModels(PaginatedDto)
export class CatsController {}

如果你现在运行 Swagger,生成的Swagger。针对这个特定端点的 Json应该定义如下响应:

"responses": {
  "200": {
    "description": "",
    "content": {
      "application/json": {
        "schema": {
          "allOf": [
            {
              "$ref": "#/components/schemas/PaginatedDto"
            },
            {
              "properties": {
                "results": {
                  "$ref": "#/components/schemas/CatDto"
                }
              }
            }
          ]
        }
      }
    }
  }
}

为了使它可重用,我们可以为PaginatedDto创建一个自定义装饰器,如下所示:

export const ApiPaginatedResponse = <TModel extends Type<any>>(
  model: TModel,
) => {
  return applyDecorators(
    ApiOkResponse({
      schema: {
        allOf: [
          { $ref: getSchemaPath(PaginatedDto) },
          {
            properties: {
              results: {
                type: 'array',
                items: { $ref: getSchemaPath(model) },
              },
            },
          },
        ],
      },
    }),
  );
};

Hint

Type<any>接口和applyDecorators函数是从@nestjs/common包中导入的。

有了这些,我们可以在端点上使用自定义的@ApiPaginatedResponse()装饰器:

@ApiPaginatedResponse(CatDto)
async findAll(): Promise<PaginatedDto<CatDto>> {}

对于客户端生成工具,这种方法在如何为客户端生成PaginatedResponse<TModel>方面造成了歧义。 下面的代码片段是上述GET /端点的客户机生成器结果示例。

// Angular
findAll(): Observable<{ total: number, limit: number, offset: number, results: CatDto[] }>

如您所见,这里的 返回类型 是不明确的。 为了解决这个问题,你可以在ApiPaginatedResponseschema中添加一个title属性:

export const ApiPaginatedResponse = <TModel extends Type<any>>(model: TModel) => {
  return applyDecorators(
    ApiOkResponse({
      schema: {
        title: `PaginatedResponseOf${model.name}`
        allOf: [
          // ...
        ],
      },
    }),
  );
};

现在客户端生成器工具的结果将变成:

// Angular
findAll(): Observable<PaginatedResponseOfCatDto>