Skip to content

类型与参数

SwaggerModule 会搜索路由处理程序中所有的 @Body()@Query()@Param() 装饰器来生成 API 文档。它还利用反射机制创建相应的模型定义。请看以下代码:

typescript
@Post()
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

提示

要显式设置请求体定义,请使用 @ApiBody() 装饰器(从 @nestjs/swagger 包导入)。

基于 CreateCatDto,Swagger UI 将创建以下模型定义:

如你所见,虽然该类声明了一些属性,但定义仍然是空的。为了让 SwaggerModule 能够识别类的属性,我们必须使用 @ApiProperty() 装饰器来注解它们,或者使用 CLI 插件(详见插件章节),它可以自动完成这些工作:

typescript
import { ApiProperty } from '@nestjs/swagger';

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

  @ApiProperty()
  age: number;

  @ApiProperty()
  breed: string;
}

提示

与其手动为每个属性添加注解,不如考虑使用 Swagger 插件(参见插件章节),它会自动为你完成这些工作。

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

此外,@ApiProperty() 装饰器允许设置各种 Schema Object 属性:

typescript
@ApiProperty({
  description: 'The age of a cat',
  minimum: 1,
  default: 1,
})
age: number;

提示

除了显式编写 @ApiProperty({ required: false }),你还可以使用简写装饰器 @ApiPropertyOptional()

要显式设置属性的类型,请使用 type 键:

typescript
@ApiProperty({
  type: Number,
})
age: number;

数组

当属性是数组时,我们必须手动指定数组类型,如下所示:

typescript
@ApiProperty({ type: [String] })
names: string[];

提示

考虑使用 Swagger 插件(参见插件章节),它会自动检测数组类型。

可以将类型作为数组的第一个元素(如上所示),或者将 isArray 属性设置为 true

循环依赖

当类之间存在循环依赖时,使用惰性函数向 SwaggerModule 提供类型信息:

typescript
@ApiProperty({ type: () => Node })
node: Node;

提示

考虑使用 Swagger 插件(参见插件章节),它会自动检测循环依赖。

泛型与接口

由于 TypeScript 不会存储泛型或接口的元数据,当你在 DTO 中使用它们时,SwaggerModule 可能无法在运行时正确生成模型定义。例如,以下代码不会被 Swagger 模块正确解析:

typescript
createBulk(@Body() usersDto: CreateUserDto[])

为了克服这一限制,你可以显式设置类型:

typescript
@ApiBody({ type: [CreateUserDto] })
createBulk(@Body() usersDto: CreateUserDto[])

枚举

要标识 enum,我们必须在 @ApiProperty 上手动设置 enum 属性,并传入一个值数组。

typescript
@ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
role: UserRole;

或者,按如下方式定义一个实际的 TypeScript enum:

typescript
export enum UserRole {
  Admin = 'Admin',
  Moderator = 'Moderator',
  User = 'User',
}

然后你可以将该 enum 直接与 @Query() 参数装饰器结合 @ApiQuery() 装饰器一起使用。

typescript
@ApiQuery({ name: 'role', enum: UserRole })
async filterByRole(@Query('role') role: UserRole = UserRole.User) {}

isArray 设置为 true 时,enum 可以作为多选使用:

枚举 Schema

默认情况下,enum 属性会在 parameter 上添加 Enum 的原始定义。

yaml
- breed:
    type: 'string'
    enum:
      - Persian
      - Tabby
      - Siamese

上述规范在大多数情况下运行良好。然而,如果你使用的工具将该规范作为输入并生成客户端代码,你可能会遇到生成的代码中包含重复 enum 的问题。请看以下代码片段:

typescript
// generated client-side code
export class CatDetail {
  breed: CatDetailEnum;
}

export class CatInformation {
  breed: CatInformationEnum;
}

export enum CatDetailEnum {
  Persian = 'Persian',
  Tabby = 'Tabby',
  Siamese = 'Siamese',
}

export enum CatInformationEnum {
  Persian = 'Persian',
  Tabby = 'Tabby',
  Siamese = 'Siamese',
}

提示

上面的代码片段是使用名为 NSwag 的工具生成的。

可以看到,现在有两个完全相同的 enum。为了解决这个问题,你可以在装饰器中与 enum 属性一起传递 enumName

typescript
export class CatDetail {
  @ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
  breed: CatBreed;
}

enumName 属性使 @nestjs/swaggerCatBreed 转换为独立的 schema,从而使 CatBreed 枚举可以复用。规范将如下所示:

yaml
CatDetail:
  type: 'object'
  properties:
    ...
    - breed:
        schema:
          $ref: '#/components/schemas/CatBreed'
CatBreed:
  type: string
  enum:
    - Persian
    - Tabby
    - Siamese

提示

任何接受 enum 作为属性的装饰器同样也接受 enumName

属性值示例

你可以使用 example 键为属性设置单个示例值,如下所示:

typescript
@ApiProperty({
  example: 'persian',
})
breed: string;

如果你想提供多个示例,可以使用 examples 键,传入一个结构化的对象:

typescript
@ApiProperty({
  examples: {
    Persian: { value: 'persian' },
    Tabby: { value: 'tabby' },
    Siamese: { value: 'siamese' },
    'Scottish Fold': { value: 'scottish_fold' },
  },
})
breed: string;

原始定义

在某些特定场景下,例如深层嵌套数组或矩阵,你可能需要手动定义类型:

typescript
@ApiProperty({
  type: 'array',
  items: {
    type: 'array',
    items: {
      type: 'number',
    },
  },
})
coords: number[][];

你也可以指定原始对象 schema,如下所示:

typescript
@ApiProperty({
  type: 'object',
  properties: {
    name: {
      type: 'string',
      example: 'Error'
    },
    status: {
      type: 'number',
      example: 400
    }
  },
  required: ['name', 'status']
})
rawDefinition: Record<string, any>;

要在控制器类中手动定义输入/输出内容,请使用 schema 属性:

typescript
@ApiBody({
  schema: {
    type: 'array',
    items: {
      type: 'array',
      items: {
        type: 'number',
      },
    },
  },
})
async create(@Body() coords: number[][]) {}

额外模型

要定义未在控制器中直接引用但需要被 Swagger 模块解析的额外模型,请使用 @ApiExtraModels() 装饰器:

typescript
@ApiExtraModels(ExtraModel)
export class CreateCatDto {}

提示

你只需要对特定的模型类使用一次 @ApiExtraModels()

或者,你也可以在 SwaggerModule.createDocument() 方法中传递一个包含 extraModels 属性的选项对象,如下所示:

typescript
const documentFactory = () =>
  SwaggerModule.createDocument(app, options, {
    extraModels: [ExtraModel],
  });

要获取模型的引用($ref),请使用 getSchemaPath(ExtraModel) 函数:

typescript
'application/vnd.api+json': {
   schema: { $ref: getSchemaPath(ExtraModel) },
},

oneOf、anyOf、allOf

要组合 schema,你可以使用 oneOfanyOfallOf 关键字(了解更多)。

typescript
@ApiProperty({
  oneOf: [
    { $ref: getSchemaPath(Cat) },
    { $ref: getSchemaPath(Dog) },
  ],
})
pet: Cat | Dog;

如果你想定义一个多态数组(即数组成员跨越多个 schema),应该使用原始定义(见上文)来手动定义类型。

typescript
type Pet = Cat | Dog;

@ApiProperty({
  type: 'array',
  items: {
    oneOf: [
      { $ref: getSchemaPath(Cat) },
      { $ref: getSchemaPath(Dog) },
    ],
  },
})
pets: Pet[];

提示

getSchemaPath() 函数从 @nestjs/swagger 导入。

CatDog 都必须使用 @ApiExtraModels() 装饰器(在类级别)定义为额外模型。

Schema 名称与描述

你可能已经注意到,生成的 schema 名称基于原始模型类的名称(例如,CreateCatDto 模型会生成 CreateCatDto schema)。如果你想更改 schema 名称,可以使用 @ApiSchema() 装饰器。

示例如下:

typescript
@ApiSchema({ name: 'CreateCatRequest' })
class CreateCatDto {}

上面的模型将被转换为 CreateCatRequest schema。

默认情况下,生成的 schema 不会添加描述。你可以使用 description 属性来添加描述:

typescript
@ApiSchema({ description: 'Description of the CreateCatDto schema' })
class CreateCatDto {}

这样,描述将包含在 schema 中,如下所示:

yaml
schemas:
  CreateCatDto:
    type: object
    description: Description of the CreateCatDto schema

基于 NestJS 官方文档翻译