Skip to content

Cell - OpenAPI 组件 ​

将 @celljs/openapi 添加到依赖中,以文档化你的 @celljs/mvc 控制器的 API:应用程序在 /openapi.json 提供 OpenAPI 3.0 文档,在 /api-docs 提供 Swagger UI。文档是从路由本身构建的(路径、@Param / @Query / @Header / @Cookie / @Body、TypeScript 参数和返回类型),因此始终与代码保持同步;@Api* 装饰器添加类型无法表达的内容。示例:examples/openapi-app。

ts
import { Body, Controller, Get, Param, Post, Query } from '@celljs/mvc/lib/node';
import { ApiOperation, ApiProperty, ApiPropertyOptional, ApiResponse, ApiTags } from '@celljs/openapi';
import { IsInt, IsOptional, IsString, Min } from 'class-validator';

export class CreatePet {
    @IsString()                       // class-validator 装饰器成为了 schema:类型、必需、枚举、长度、范围...
    @ApiProperty({ example: 'Tom' })  // ...而 @ApiProperty 添加描述、示例或数组的类型
    name: string;

    @IsInt() @Min(0) @IsOptional()
    age?: number;
}

export class Pet extends CreatePet {
    @ApiProperty()
    id: number;
}

@Controller('pets')
@ApiTags('Pets')
export class PetController {
    @Get()
    @ApiResponse({ type: [Pet] })    // 异步方法和数组需要类型:TypeScript 只会发出 Promise / Array
    list(@Query() query: PetQuery): Promise<Pet[]> { ... }

    @Get(':id')
    @ApiOperation({ summary: 'Find a pet' })
    @ApiResponse({ status: 404, description: 'No such pet' })
    get(@Param('id') id: number): Pet { ... }   // 返回类型是 200 响应

    @Post()
    create(@Body() pet: CreatePet): Pet { ... } // body 的类是请求 schema
}

文档中包含的内容:

  • 路径:控制器的每个路由,:id 表示为 {id};可选部分(/users(/:id))生成一个有它的路径和一个没有它的路径。带有 RegExp 路径的路由被排除。
  • 参数:@Param('id')、@Query('q')、@Header('x-name')、@Cookie('name') 和参数的类型;@Query() query: PetQuery 为类的每个属性给出一个查询参数。方法中缺少的路径参数作为字符串添加。
  • Body:@Body() pet: CreatePet 作为 application/json body;命名的 @Body('name') 参数作为对象的属性。
  • 响应:@ApiResponse 的那些(在类上对所有方法,或在方法上);如果没有 2xx 的响应,则 200 带有返回类型。内容类型遵循视图:默认为 JSON,@Text() 为 text/plain,@Html() 为 text/html。
  • Schema:每个类成为 components.schemas 的一个条目,由 $ref 引用。其属性是那些带有 class-validator 装饰器的属性(从 class-validator 的元数据中读取,当它被安装时,例如使用 @celljs/validation;它不是此包的依赖)或带有 @ApiProperty 的属性。属性是必需的,除非它有 @IsOptional() 或 @ApiPropertyOptional()。
  • 标签:@ApiTags,否则是控制器名称(去掉 Controller)。
装饰器在目的
@ApiTags(...tags)类、方法对操作进行分组
@ApiOperation({ summary, description, operationId, deprecated })方法描述操作;operationId 默认为 <Class>_<method>
@ApiResponse({ status, description, type, schema, contentType })类、方法一个响应;按状态重复;type: [Pet] 表示列表
@ApiProperty(options) / @ApiPropertyOptional(options)属性请求或响应类的属性;type、enum(列表或 TypeScript 枚举)、description、example、format 和任何其他 schema 字段
@ApiParam / @ApiQuery / @ApiHeader({ name, ... })方法描述参数,或记录方法从请求本身读取的参数
@ApiSecurity(name, scopes?) / @ApiBearerAuth()类、方法需要安全方案;当 components.securitySchemes 中没有时,bearer 被声明为 @ApiBearerAuth()
@ApiExclude()类、方法将其从文档中排除

配置 ​

yaml
openapi:
  enabled: true                   # false:既不提供文档也不提供 UI
  url: /openapi.json              # 在 server.path 下
  ui:
    enabled: true
    url: /api-docs
    cdn: https://cdn.jsdelivr.net/npm/swagger-ui-dist@5   # 页面从中加载 Swagger UI 的位置
  document:                       # 合并到生成的文档中
    openapi: 3.0.3
    info:
      title: Pet Store
      version: 1.0.0
    servers:
      - url: https://api.example.com
    components:
      securitySchemes:
        apiKey: { type: apiKey, in: header, name: X-API-Key }

文档和页面响应任何控制器未应答的 GET 请求,因此应用程序的具有相同路径的路由赢得,其他请求无需付出任何代价。除非 @celljs/security 保护它们,否则是公开的;要在生产中隐藏它们,在生产配置中设置 enabled: false(cell.production.yml)。

要写文档到文件而不启动服务器(用于 API 客户端、代码生成器或审核),从 @celljs/cli 的 createTestApp() 中获取它,如示例的 scripts/export-openapi.ts 所做。

OpenApiDocumentProvider(@celljs/openapi/lib/node)在代码中提供文档;重新绑定它以改变构建方式。