OPENAPI
介绍
OpenAPI
是一个与语言无关的RESTful API
定义说明,Nest
提供了一个专有的模块来利用装饰器生成类似声明。
安装
要开始使用,首先安装依赖、
$ npm install --save @nestjs/swagger swagger-ui-express
如果使用fastify,安装fastify-swagger
而不是swagger-ui-express
:
$ npm install --save @nestjs/swagger fastify-swagger
引导
安装完成后,在main.ts
文件中定义并初始化SwaggerModule
类:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
文档(通过
SwaggerModule#createDocument()
方法返回)是一个遵循OpenAPI文档的序列化对象。除了HTTP,你也可以以JSON/YAML文件格式保存和使用它。
DocumentBuilder
建立一个遵循OpenAPI 标准的基础文档。它提供了不同的方法来配置类似标题、描述、版本等信息属性。要创建一个完整的文档(使用HTTP定义),我们使用SwaggerModule
类的createDocument()
方法。这个方法有两个参数,一个应用实例和一个Swagger选项对象。我们也可以提供第三个SwaggerDocumentOptions
类型可选对象,见文档选项。
创建文档后,调用setup()
方法,它接受:
- 挂载Swagger界面的路径。
- 应用实例。
- 上述实例化的文档对象。
运行以下命令启动HTTP服务器。
$ npm run start
浏览http://localhost:3000/api
可以看到Swagger界面。
Swagger模块自动反射你所有的终端。注意Swagger界面根据平台不同,由swagger-ui-express
或fastify-swagger
生成。
要生成和下载一个Swagger JSON文件,导航到
http://localhost:3000/api-json
(swagger-ui-express
) 或http://localhost:3000/api/json
(fastify-swagger
) (假设API文档在 http://localhost:3000/api路径)。
!> 在使用fastify-swagger
和helmet
时可能有CSP问题,要处理这个冲突,参考如下配置CSP。
app.register(helmet, {
contentSecurityPolicy: {
directives: {
defaultSrc: [`'self'`],
styleSrc: [`'self'`, `'unsafe-inline'`],
imgSrc: [`'self'`, 'data:', 'validator.swagger.io'],
scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
},
},
});
// If you are not going to use CSP at all, you can use this: app.register(helmet, { contentSecurityPolicy: false, });
文档选项
创建文档时,可以提供一些额外选项来配合库特性。这些选项应该是SwaggerDocumentOptions
类型:
export interface SwaggerDocumentOptions {
/**
* List of modules to include in the specification
*/
include?: Function[];
/**
* Additional, extra models that should be inspected and included in the specification
*/
extraModels?: Function[];
/**
* If `true`, swagger will ignore the global prefix set through `setGlobalPrefix()` method
*/
ignoreGlobalPrefix?: boolean;
/**
* If `true`, swagger will also load routes from the modules imported by `include` modules
*/
deepScanRoutes?: boolean;
/**
* Custom operationIdFactory that will be used to generate the `operationId`
* based on the `controllerKey` and `methodKey`
* @default () => controllerKey_methodKey
*/
operationIdFactory?: (controllerKey: string, methodKey: string) => string;
}
例如,如果你要确保库像createUser
而不是UserController_createUser
一样生成操作名称,可以做如下配置:
const options: SwaggerDocumentOptions = {
operationIdFactory: (
controllerKey: string,
methodKey: string
) => methodKey
});
const document = SwaggerModule.createDocument(app, config, options);
示例
一个例子见这里。
类型和参数
SwaggerModule
在路径处理程序上搜索所有@Body()
, @Query()
, 以及@Param()
装饰器来生成API文档。它也利用反射来创建响应模型。考虑以下代码:
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
要显式定义主体,使用
@ApiBody()
装饰器 (从@nestjs/swagger
引入).
基于CreateCatDto
,将创建以下Swagger页面模型。
如你所见,虽然类已经声明了一些属性,但这里的定义是空的。要使这些类属性在SwaggerModule
中可见,我们要么用@ApiProperty()
装饰器或使用CLI
插件来自动生成:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
考虑使用Swagger插件(参见CLI插件)来自动生成以代替手动装饰每个属性。
打开浏览器确认生成的CreateCatDto
模型:
@ApiProperty()
装饰器也允许设置不同的原型对象属性:
@ApiProperty({
description: 'The age of a cat',
minimum: 1,
default: 1,
})
age: number;
可以使用
@ApiPropertyOptional()
速记装饰器来替代显式输入@ApiProperty({ required: false })
。
要显式指定属性类型,使用type
字段:
@ApiProperty({
type: Number,
})
age: number;
数组
当属性是数组时,我们必须手动指定数组类型:
@ApiProperty({ type: [String] })
names: string[];
考虑使用Swagger 插件来自动发现数组
要么将类型作为数组的第一个元素(如上),要么将isArray
属性设为true
。
循环依赖
当你的类之间有循环依赖时,使用SwaggerModul
提供的一个包含类型信息的懒函数。
@ApiProperty({ type: () => Node })
node: Node;
考虑使用Swagger 插件来自动发现循环依赖
泛型和接口
由于TypeScript
没有存储泛型或者接口的元数据,当你在DTO中使用他们的时候,SwaggerModule
可能不会正确生成运行时的模型定义。基于此,下列代码不会被Swagger模块正确识别。
createBulk(@Body() usersDto: CreateUserDto[])
要处理这些限制,需要显式配置类型:
@ApiBody({ type: [CreateUserDto] })
createBulk(@Body() usersDto: CreateUserDto[])
枚举
要定义一个枚举,需要在@ApiProperty
中用数组手动设置enum
属性。
@ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
role: UserRole;
也可以如下定义一个真实的TypeScript
泛型:
export enum UserRole {
Admin = 'Admin',
Moderator = 'Moderator',
User = 'User',
}
可以在@Query()
参数中配合@ApiQuery()
装饰器直接使用enum
:
@ApiQuery({ name: 'role', enum: UserRole })
async filterByRole(@Query('role') role: UserRole = UserRole.User) {}
当isArray
配置为true
时, enum
可以多选。
枚举原型
默认地,enum
属性将为Enum在parameter
上添加一个原始定义。
- breed:
type: 'string'
enum:
- Persian
- Tabby
- Siamese
上述定义在大部分情况下工作良好。然而,如果你使用该定义作为输入在客户端生成代码时,可能会遇到属性包含重复枚举的情况,考虑以下代码:
// 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
属性中传入enumName
参数。
export class CatDetail {
@ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
breed: CatBreed;
}
enumName
属性使能@nestjs/swagger
来将CatBreed
转换为其原型,从而使CatBreed
可重用:
CatDetail:
type: 'object'
properties:
...
- breed:
schema:
$ref: '#/components/schemas/CatBreed'
CatBreed:
type: string
enum:
- Persian
- Tabby
- Siamese
任何包含
enum
属性的装饰器都有enumName
原始定义
在一些特殊场合(例如深度嵌套的数组和矩阵),你可能需要手动描述你的类型。
@ApiProperty({
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
})
coords: number[][];
类似地,要在控制器类中手动定义输入输出,使用schema
属性:
@ApiBody({
schema: {
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
},
})
async create(@Body() coords: number[][]) {}
额外模型
要定义控制器中没有直接使用,但是需要被Swagger模块检查的额外的模型,使用@ApiExtraModels()
装饰器:
@ApiExtraModels(ExtraModel)
export class CreateCatDto {}
只需要对指定的model类使用一次
@ApiExtraModels()
你也可以把一个选项对象和extraModels
属性一起传递给SwaggerModule#createDocument()
方法:
const document = SwaggerModule.createDocument(app, options, {
extraModels: [ExtraModel],
});
要获得一个模型的引用($ref
) ,使用getSchemaPath(ExtraModel)
函数:
'application/vnd.api+json': {
schema: { $ref: getSchemaPath(ExtraModel) },
},
oneOf
, anyOf
, allOf
要组合原型,你可以使用oneOf
,anyOf
或者allOf
关键词(阅读更多).
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
})
pet: Cat | Dog;
如果你要定义一个多态数组(例如,数组成员跨越多个原型),你应该使用前节的原始定义来手动定义你的类型。
type Pet = Cat | Dog;
@ApiProperty({
type: 'array',
items: {
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
},
})
pets: Pet[];
getSchemaPath()
函数从@nestjs/swagger
引入.
Cat
和Dog
都应该使用@ApiExtraModels()
装饰器 (在类水平).
操作
在OpenAPI规范中,API暴露的以{资源}为结束的终端,例如/users
或者/reports/summary
,都是可以执行HTTP方法的,例如GET
,POST
或者DELETE
。
标签
要为控制器附加一个标签,使用`@ApiTags(…tags)装饰器。
@ApiTags('cats')
@Controller('cats')
export class CatsController {}
报头
要作为请求的一部分定义自定义报头,使用@ApiHeader()
装饰器。
@ApiHeader({
name: 'X-MyHeader',
description: 'Custom header',
})
@Controller('cats')
export class CatsController {}
响应
要定义一个自定义响应, 使用`@ApiResponse()装饰器.
@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提供了一系列继承自@ApiResponse装饰器的用于速记的API响应装饰器:
- @ApiOkResponse()
- @ApiCreatedResponse()
- @ApiAcceptedResponse()
- @ApiNoContentResponse()
- @ApiMovedPermanentlyResponse()
- @ApiBadRequestResponse()
- @ApiUnauthorizedResponse()
- @ApiNotFoundResponse()
- @ApiForbiddenResponse()
- @ApiMethodNotAllowedResponse()
- @ApiNotAcceptableResponse()
- @ApiRequestTimeoutResponse()
- @ApiConflictResponse()
- @ApiTooManyRequestsResponse()
- @ApiGoneResponse()
- @ApiPayloadTooLargeResponse()
- @ApiUnsupportedMediaTypeResponse()
- @ApiUnprocessableEntityResponse()
- @ApiInternalServerErrorResponse()
- @ApiNotImplementedResponse()
- @ApiBadGatewayResponse()
- @ApiServiceUnavailableResponse()
- @ApiGatewayTimeoutResponse()
- @ApiDefaultResponse()
@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()
来使能文件上传,这里有一个完整的使用文件上传技术的例子。
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'List of cats',
type: FileUploadDto,
})
uploadFile(@UploadedFile() file) {}
FileUploadDto
像这样定义:
class FileUploadDto {
@ApiProperty({ type: 'string', format: 'binary' })
file: any;
}
要处理多个文件上传,如下定义FilesUploadDto
:
class FilesUploadDto {
@ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } })
files: any[];
}
扩展
要为请求增加一个扩展使用@ApiExtension()
装饰器. 该扩展名称必须以 x-
前缀。
@ApiExtension('x-foo', { hello: 'world' })
高级主题:通用ApiResponse
基于原始定义,的能力,我们可以为Swagger定义通用原型:
export class PaginatedDto<TData> {
@ApiProperty()
total: number;
@ApiProperty()
limit: number;
@ApiProperty()
offset: number;
results: TData[];
}
我们跳过了定义results
,因为后面要提供一个原始定义。现在,我们定义另一个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
属性类型为CatDto
数组。
getSchemaPath()
函数从一个给定模型的OpenAPI指定文件返回OpenAPI原型路径allOf
是一个OAS3的概念,包括各种各样相关用例的继承。
最后,因为PaginatedDto
没有被任何控制器直接引用,SwaggerModule
还不能生成一个相应的模型定义。我们需要一个额外的模型,可以在控制器水平使用@ApiExtraModels()
装饰器。
@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) },
},
},
},
],
},
}),
);
};
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[] }>
可以看出,这里的返回类型是含糊不清的。要处理这个问题,可以为ApiPaginatedResponse
的原型
添加title
属性。
export const ApiPaginatedResponse = <TModel extends Type<any>>(model: TModel) => {
return applyDecorators(
ApiOkResponse({
schema: {
title: `PaginatedResponseOf${model.name}`
allOf: [
// ...
],
},
}),
);
};
现在结果变成了。
// Angular
findAll(): Observable<PaginatedResponseOfCatDto>
安全
要确定某个特定操作使用哪个安全机制,使用@ApiSecurity()
装饰器。
@ApiSecurity('basic')
@Controller('cats')
export class CatsController {}
在运行程序前,使用DocumentBuilder
在基础文档里添加安全定义。
const options = new DocumentBuilder().addSecurity('basic', {
type: 'http',
scheme: 'basic',
});
一些最常用的认证机制是内置的(例如basic
和bearer
),因此不需要像上面那样手动定义。
Basic
认证
使用@ApiBasicAuth()
配置basic认证。
@ApiBasicAuth()
@Controller('cats')
export class CatsController {}
在运行程序前,使用DocumentBuilder
在基础文档里添加安全定义。
const options = new DocumentBuilder().addBasicAuth();
Bearer认证
使用@ApiBearerAuth()
启用bearer认证。
@ApiBearerAuth()
@Controller('cats')
export class CatsController {}
在运行程序前,使用DocumentBuilder
在基础文档里添加安全定义。
const options = new DocumentBuilder().addBearerAuth();
OAuth2认证
使用@ApiOAuth2()
启用OAuth2认证。
@ApiOAuth2(['pets:write'])
@Controller('cats')
export class CatsController {}
在运行程序前,使用DocumentBuilder
在基础文档里添加安全定义。
const options = new DocumentBuilder().addOAuth2();
Cookie认证
使用@ApiCookieAuth()
启用cookie认证。
@ApiCookieAuth()
@Controller('cats')
export class CatsController {}
在运行程序前,使用DocumentBuilder
在基础文档里添加安全定义。
const options = new DocumentBuilder().addCookieAuth('optional-session-id');
映射的类型
像构建CRUD特性一样,通常需要基于实体类型创建变体。Nest提供了一些应用函数来进行类型变换,以让这类变换工作更简单。
Partial(部分声明)
在创建数据转换对象(也称为DTO),将创建
和更新
创建为同一类型通常很有用。例如,创建变体可能需要所有字段,但更新变体可能将所有字段都配置为可选的。
Nest提供了PartialType()
应用函数让这一任务更简单地最小化构造。
PartialType()
函数返回一个类型(类)
将输入的所有属性配置为可选的。例如,你可以这样创建一个类型。
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
默认所有的字段都是必须的。要创建一个所有字段与之相同但都是可选的字段,使用PartialType()
并将CreateCatDto
作为参数。
export class UpdateCatDto extends PartialType(CreateCatDto) {}
PartialType()
函数从@nestjs/swagger
引入.
Pick(拾取)
PickType()
函数从输入类型中拾取一部分属性并生成一个新类型(类) 。假设我们起始类如下:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
我们使用PickType()
从中拾取一部分属性:
export class UpdateCatAgeDto extends PickType(CreateCatDto, ['age'] as const) {}
PickType()
函数 从@nestjs/swagger
引入.
Omit(省略)
OmitType()
函数拾取所有输入属性,移除指定部分属性。例如,我们起始类型如下:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
我们可以以此创建一个除name
之外的包含其他所有属性的类。OmitType
函数的第二个参数是包含要移除属性名称的数组。
export class UpdateCatDto extends OmitType(CreateCatDto, ['name'] as const) {}
OmitType()
函数从@nestjs/swagger
引入.
Intersection(交叉)
IntersectionType()
函数将两个类型组合为一个类型(类),例如,我们起始的两个类型如下:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
breed: string;
}
export class AdditionalCatInfo {
@ApiProperty()
color: string;
}
我们可以生成一个由两个类中所有属性组成的新类型。
export class UpdateCatDto extends IntersectionType(
CreateCatDto,
AdditionalCatInfo,
) {}
?>IntersectionType()
函数从@nestjs/swagger
引入.
Composition(组合)
映射类型的使用时可以组合的,例如,以下代码创建一个类型(类),它包含了CreateCatDto
除了name
之外的所有属性,并将所有属性设置为可选的。
export class UpdateCatDto extends PartialType(
OmitType(CreateCatDto, ['name'] as const),
) {}
装饰器
所有可用的OpenAPI装饰器都有Api前缀用以和核心装饰器区分。下面是完整的装饰器名称列表以及其可能能应用的范围。
名称 | 类型 |
---|---|
@ApiOperation()|Method @ApiResponse()|Method / Controller @ApiProduces()|Method / Controller @ApiConsumes()|Method / Controller @ApiBearerAuth()|Method / Controller @ApiOAuth2()|Method / Controller @ApiBasicAuth()|Method / Controller @ApiSecurity()|Method / Controller @ApiExtraModels()|Method / Controller @ApiBody()|Method @ApiParam()|Method @ApiQuery()|Method @ApiHeader()|Method / Controller @ApiExcludeEndpoint()|Method @ApiTags()|Method / Controller @ApiProperty()|Model @ApiPropertyOptional()|Model @ApiHideProperty()|Model @ApiExtension()|Method
CLI插件
TypeScript的元数据反射系统有一些限制,一些功能因此不可能实现,例如确定一个类由哪些属性组成,或者一个属性是可选的还是必须的。然而,一些限制可以在编译时强调。Nest提供了一个增强TypeScript编译过程的插件来减少需要的原型代码量。
这个插件是一个
opt-in
,你也可以选择手动声明所有的装饰器,或者仅仅声明你需要的。
概述
Swagger插件可以自动:
- 使用
@ApiProperty
注释所有除了用@ApiHideProperty
装饰的DTO属性。 - 根据问号符号确定
required
属性(例如name?: string
将设置required: false
) - 根据类型配置
type
为enum
(也支持数组) - 基于给定的默认值配置默认参数
- 基于
class-validator
装饰器配置一些验证策略(如果classValidatorShim
配置为true
) - 为每个终端添加一个响应装饰器,包括合适的状态和类型(响应模式)
- 根据注释生成属性和终端的描述(如果
introspectComments
配置为true
) - 基于注释生成属性的示例数据(如果
introspectComments
配置为true
)
注意,你的文件名必须有如下后缀: ['.dto.ts', '.entity.ts']
(例如create-user.dto.ts
) 才能被插件分析。
如果使用其他后缀,你可以调整插件属性来指定dtoFileNameSuffix
选项(见下文)。
之前,如果你想要通过Swagger提供一个交互体验,你必须复制大量代码让包知道你的模型/组件在该声明中。例如,你可以定义一个CreateUserDto
类:
export class CreateUserDto {
@ApiProperty()
email: string;
@ApiProperty()
password: string;
@ApiProperty({ enum: RoleEnum, default: [], isArray: true })
roles: RoleEnum[] = [];
@ApiProperty({ required: false, default: true })
isEnabled?: boolean = true;
}
在中等项目中这还不是问题,但是一旦有大量类的话这就变得冗余而难以维护。
要应用Swagger插件,可以简单声明上述类定义:
export class CreateUserDto {
email: string;
password: string;
roles: RoleEnum[] = [];
isEnabled?: boolean = true;
}
插件可以通过抽象语法树添加合适的装饰器,你不在需要在代码中到处写ApiProperty
装饰器。
插件可以自动生成所有缺失的swagger属性,但是如果你要覆盖他们,只需要通过
@ApiProperty()
显式声明即可。
注释自省
注释自省特性使能后,CLI插件可以基于注释生成描述和示例值。
例如,一个给定的roles
属性示例:
/**
* A list of user's roles
* @example ['admin']
*/
@ApiProperty({
description: `A list of user's roles`,
example: ['admin'],
})
roles: RoleEnum[] = [];
你必须复制描述和示例值。当introspectComments
使能后,CLI插件可以自动解压这些注释并提供描述(以及示例,如果定义了的话)。现在,上述属性可以简化为:
/**
* A list of user's roles
* @example ['admin']
*/
roles: RoleEnum[] = [];
使用CLI插件
要使能CLI插件,打开nest-cli.json
(如果你在用Nest CLI)并添加以下插件配置:
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger"]
}
}
你可以使用其他options
属性来自定义插件特性。
"plugins": [
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": false,
"introspectComments": true
}
}
]
options
属性实现以下接口:
export interface PluginOptions {
dtoFileNameSuffix?: string[];
controllerFileNameSuffix?: string[];
classValidatorShim?: boolean;
introspectComments?: boolean;
}
选项 | 默认 | 说明 |
---|---|---|
dtoFileNameSuffix | [‘.dto.ts’, ‘.entity.ts’] | DTO (数据传输对象)文件后缀 |
controllerFileNameSuffix | .controller.ts | 控制文件后缀 |
classValidatorShim | true | 如果配置为true ,模块将重用class-validator 验证装饰器 (例如@Max(10) 将在schema 定义中增加max: 10 ) |
introspectComments | false | 如果配置为true ,插件将根据描述注释生成说明和示例 |
如果不使用CLI,但是使用一个用户定义的Webpack
配置,可以和ts-loader
配合使用该插件:
getCustomTransformers: (program: any) => ({
before: [require('@nestjs/swagger/plugin').before({}, program)]
}),
和ts-jest
(e2e)
要运行e2e测试,ts-jest
在内存汇总编译源码,这意味着不使用Nest Cli编译,不应用任何插件或AST转换,要使用插件,在e2e测试目录下创建以下文件:
const transformer = require('@nestjs/swagger/plugin');
module.exports.name = 'nestjs-swagger-transformer';
// you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
module.exports.version = 1;
module.exports.factory = (cs) => {
return transformer.before(
{
// @nestjs/swagger/plugin options (can be empty)
},
cs.tsCompiler.program,
);
};
在jest配置文件中引入AST变换。默认在(启动应用中),e2e测试配置文件在测试目录下,名为jest-e2e.json
。
{
... // other configuration
"globals": {
"ts-jest": {
"astTransformers": {
"before": ["<path to the file created above>"],
}
}
}
}
其他特性
全局前缀
要忽略一个通过setGlobalPrefix()
配置的全局前缀, 使用ignoreGlobalPrefix
:
const document = SwaggerModule.createDocument(app, options, {
ignoreGlobalPrefix: true,
});
多重声明
Swagger
模块提供了一个支持多重声明的方法,也就是说可以在多个终端提供多个界面和多个文档。
要支持多重声明,首先在模块中要进行声明,在createDocument()
方法中传递第3个参数,extraOptions
,这是个包含一个叫做include
名称的属性,该属性提供了一个由模块组成的数组。
可以如下配置多重声明:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
/**
* createDocument(application, configurationOptions, extraOptions);
*
* createDocument method takes an optional 3rd argument "extraOptions"
* which is an object with "include" property where you can pass an Array
* of Modules that you want to include in that Swagger Specification
* E.g: CatsModule and DogsModule will have two separate Swagger Specifications which
* will be exposed on two different SwaggerUI with two different endpoints.
*/
const options = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const catDocument = SwaggerModule.createDocument(app, options, {
include: [CatsModule],
});
SwaggerModule.setup('api/cats', app, catDocument);
const secondOptions = new DocumentBuilder()
.setTitle('Dogs example')
.setDescription('The dogs API description')
.setVersion('1.0')
.addTag('dogs')
.build();
const dogDocument = SwaggerModule.createDocument(app, secondOptions, {
include: [DogsModule],
});
SwaggerModule.setup('api/dogs', app, dogDocument);
await app.listen(3000);
}
bootstrap();
现在可以使用以下命令启动服务器:
$ npm run start
访问http://localhost:3000/api/cats
可以看到cats
的Swagger
界面,
访问http://localhost:3000/api/dogs
可以看到dogs
的Swagger
界面。
迁移指南
如果你在使用@nestjs/swagger@3.*
, 注意在4.0版本中有以下破坏性变化或者更改。
破坏性变化
以下装饰器被改变/重命名
@ApiModelProperty
现在是@ApiProperty
@ApiModelPropertyOptional
现在是@ApiPropertyOptional
@ApiResponseModelProperty
现在是@ApiResponseProperty
@ApiImplicitQuery
现在是@ApiQuery
@ApiImplicitParam
现在是@ApiParam
@ApiImplicitBody
现在是@ApiBody
@ApiImplicitHeader
现在是@ApiHeader
@ApiOperation({ title: 'test' })
现在是@ApiOperation({ summary: 'test' })
@ApiUseTags
现在是@ApiTags
DocumentBuilder
的破坏性更新(升级了方法签名):
- addTag
- addBearerAuth
- addOAuth2
- setContactEmail
现在是
setContact - setHost has been removed
- setSchemes has been removed (使用`addServer instead, e.g., addServer(‘http://‘))
新方法
添加了以下新方法:
- addServer
- addApiKey
- addBasicAuth
- addSecurity
- addSecurityRequirements
译者署名
| 用户名 | 头像 | 职能 | 签名 | |—-|—-|—-|—-|