OPENAPI

    要开始使用,首先安装依赖、

    如果使用fastify,安装fastify-swagger而不是swagger-ui-express:

    1. $ npm install --save @nestjs/swagger fastify-swagger

    引导

    安装完成后,在main.ts文件中定义并初始化SwaggerModule类:

    1. import { NestFactory } from '@nestjs/core';
    2. import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
    3. import { AppModule } from './app.module';
    4. async function bootstrap() {
    5. const app = await NestFactory.create(AppModule);
    6. const config = new DocumentBuilder()
    7. .setTitle('Cats example')
    8. .setDescription('The cats API description')
    9. .setVersion('1.0')
    10. .addTag('cats')
    11. .build();
    12. const document = SwaggerModule.createDocument(app, config);
    13. SwaggerModule.setup('api', app, document);
    14. await app.listen(3000);
    15. }
    16. bootstrap();

    DocumentBuilder建立一个遵循OpenAPI 标准的基础文档。它提供了不同的方法来配置类似标题、描述、版本等信息属性。要创建一个完整的文档(使用HTTP定义),我们使用SwaggerModule类的createDocument()方法。这个方法有两个参数,一个应用实例和一个Swagger选项对象。我们也可以提供第三个SwaggerDocumentOptions类型可选对象,见。

    创建文档后,调用setup()方法,它接受:

    1. 挂载Swagger界面的路径。
    2. 应用实例。
    3. 上述实例化的文档对象。

    运行以下命令启动HTTP服务器。

    1. $ npm run start

    浏览http://localhost:3000/api可以看到Swagger界面。

    Swagger模块自动反射你所有的终端。注意Swagger界面根据平台不同,由swagger-ui-expressfastify-swagger生成。

    要生成和下载一个Swagger JSON文件,导航到http://localhost:3000/api-json (swagger-ui-express) 或http://localhost:3000/api/json (fastify-swagger) (假设API文档在 http://localhost:3000/api路径)。

    !> 在使用fastify-swaggerhelmet时可能有CSP问题,要处理这个冲突,参考如下配置CSP。

    1. app.register(helmet, {
    2. contentSecurityPolicy: {
    3. directives: {
    4. defaultSrc: [`'self'`],
    5. styleSrc: [`'self'`, `'unsafe-inline'`],
    6. imgSrc: [`'self'`, 'data:', 'validator.swagger.io'],
    7. scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
    8. },
    9. },
    10. });

    // If you are not going to use CSP at all, you can use this: app.register(helmet, { contentSecurityPolicy: false, });

    文档选项

    创建文档时,可以提供一些额外选项来配合库特性。这些选项应该是SwaggerDocumentOptions类型:

    1. export interface SwaggerDocumentOptions {
    2. /**
    3. * List of modules to include in the specification
    4. */
    5. include?: Function[];
    6. /**
    7. * Additional, extra models that should be inspected and included in the specification
    8. */
    9. extraModels?: Function[];
    10. /**
    11. * If `true`, swagger will ignore the global prefix set through `setGlobalPrefix()` method
    12. */
    13. ignoreGlobalPrefix?: boolean;
    14. /**
    15. * If `true`, swagger will also load routes from the modules imported by `include` modules
    16. */
    17. deepScanRoutes?: boolean;
    18. /**
    19. * Custom operationIdFactory that will be used to generate the `operationId`
    20. * based on the `controllerKey` and `methodKey`
    21. * @default () => controllerKey_methodKey
    22. */
    23. operationIdFactory?: (controllerKey: string, methodKey: string) => string;
    24. }

    例如,如果你要确保库像createUser而不是UserController_createUser一样生成操作名称,可以做如下配置:

    1. const options: SwaggerDocumentOptions = {
    2. operationIdFactory: (
    3. controllerKey: string,
    4. methodKey: string
    5. ) => methodKey
    6. });
    7. const document = SwaggerModule.createDocument(app, config, options);

    示例

    一个例子见。

    类型和参数

    SwaggerModule在路径处理程序上搜索所有@Body(), @Query(), 以及@Param()装饰器来生成API文档。它也利用反射来创建响应模型。考虑以下代码:

    1. @Post()
    2. async create(@Body() createCatDto: CreateCatDto) {
    3. this.catsService.create(createCatDto);
    4. }

    要显式定义主体,使用@ApiBody()装饰器 (从@nestjs/swagger引入).

    基于CreateCatDto,将创建以下Swagger页面模型。

    OPENAPI - 图2

    如你所见,虽然类已经声明了一些属性,但这里的定义是空的。要使这些类属性在SwaggerModule中可见,我们要么用@ApiProperty()装饰器或使用CLI插件来自动生成:

    1. import { ApiProperty } from '@nestjs/swagger';
    2. export class CreateCatDto {
    3. @ApiProperty()
    4. name: string;
    5. @ApiProperty()
    6. age: number;
    7. @ApiProperty()
    8. breed: string;
    9. }

    考虑使用Swagger插件(参见)来自动生成以代替手动装饰每个属性。

    打开浏览器确认生成的CreateCatDto模型:

    @ApiProperty()装饰器也允许设置不同的原型对象属性:

    1. @ApiProperty({
    2. description: 'The age of a cat',
    3. minimum: 1,
    4. default: 1,
    5. })
    6. age: number;

    可以使用@ApiPropertyOptional()速记装饰器来替代显式输入@ApiProperty({ required: false })

    要显式指定属性类型,使用type字段:

    1. @ApiProperty({
    2. type: Number,
    3. })
    4. age: number;

    数组

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

    1. @ApiProperty({ type: [String] })
    2. names: string[];

    要么将类型作为数组的第一个元素(如上),要么将isArray属性设为true

    循环依赖

    当你的类之间有循环依赖时,使用SwaggerModul提供的一个包含类型信息的懒函数。

    1. @ApiProperty({ type: () => Node })
    2. node: Node;

    考虑使用Swagger 插件来自动发现循环依赖

    泛型和接口

    由于TypeScript没有存储泛型或者接口的元数据,当你在DTO中使用他们的时候,SwaggerModule可能不会正确生成运行时的模型定义。基于此,下列代码不会被Swagger模块正确识别。

    1. createBulk(@Body() usersDto: CreateUserDto[])

    要处理这些限制,需要显式配置类型:

    1. @ApiBody({ type: [CreateUserDto] })
    2. createBulk(@Body() usersDto: CreateUserDto[])

    枚举

    要定义一个枚举,需要在@ApiProperty中用数组手动设置enum属性。

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

    也可以如下定义一个真实的TypeScript泛型:

    1. export enum UserRole {
    2. Admin = 'Admin',
    3. Moderator = 'Moderator',
    4. User = 'User',
    5. }

    可以在@Query()参数中配合@ApiQuery()装饰器直接使用enum

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

    OPENAPI - 图4

    isArray配置为true时, enum可以多选。

    枚举原型

    默认地,enum属性将为在parameter上添加一个原始定义。

    1. - breed:
    2. type: 'string'
    3. enum:
    4. - Persian
    5. - Tabby
    6. - Siamese

    上述定义在大部分情况下工作良好。然而,如果你使用该定义作为输入在客户端生成代码时,可能会遇到属性包含重复枚举的情况,考虑以下代码:

    1. // generated client-side code
    2. export class CatDetail {
    3. breed: CatDetailEnum;
    4. }
    5. export class CatInformation {
    6. breed: CatInformationEnum;
    7. }
    8. export enum CatDetailEnum {
    9. Persian = 'Persian',
    10. Tabby = 'Tabby',
    11. Siamese = 'Siamese',
    12. }
    13. export enum CatInformationEnum {
    14. Persian = 'Persian',
    15. Tabby = 'Tabby',
    16. Siamese = 'Siamese',
    17. }

    上述代码使用NSwag工具生成

    现在可以看到有两个枚举完全一样,要处理这个问题,需要在装饰器的enum属性中传入enumName参数。

    1. export class CatDetail {
    2. @ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
    3. breed: CatBreed;
    4. }

    enumName属性使能@nestjs/swagger来将CatBreed转换为其原型,从而使CatBreed可重用:

    1. CatDetail:
    2. type: 'object'
    3. properties:
    4. ...
    5. - breed:
    6. schema:
    7. $ref: '#/components/schemas/CatBreed'
    8. CatBreed:
    9. type: string
    10. enum:
    11. - Persian
    12. - Tabby
    13. - Siamese

    任何包含enum属性的装饰器都有enumName

    原始定义

    1. @ApiProperty({
    2. type: 'array',
    3. items: {
    4. type: 'array',
    5. items: {
    6. type: 'number',
    7. },
    8. },
    9. })
    10. coords: number[][];

    类似地,要在控制器类中手动定义输入输出,使用schema属性:

    1. @ApiBody({
    2. schema: {
    3. type: 'array',
    4. items: {
    5. type: 'array',
    6. items: {
    7. type: 'number',
    8. },
    9. },
    10. },
    11. })
    12. async create(@Body() coords: number[][]) {}

    额外模型

    要定义控制器中没有直接使用,但是需要被Swagger模块检查的额外的模型,使用@ApiExtraModels()装饰器:

    1. @ApiExtraModels(ExtraModel)
    2. export class CreateCatDto {}

    只需要对指定的model类使用一次@ApiExtraModels()

    你也可以把一个选项对象和extraModels属性一起传递给SwaggerModule#createDocument() 方法:

    1. extraModels: [ExtraModel],
    2. });

    要获得一个模型的引用($ref) ,使用getSchemaPath(ExtraModel)函数:

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

    oneOf, anyOf, allOf

    要组合原型,你可以使用oneOf,anyOf 或者allOf关键词(阅读更多).

    如果你要定义一个多态数组(例如,数组成员跨越多个原型),你应该使用前节的原始定义来手动定义你的类型。

    1. type Pet = Cat | Dog;
    2. type: 'array',
    3. items: {
    4. oneOf: [
    5. { $ref: getSchemaPath(Cat) },
    6. { $ref: getSchemaPath(Dog) },
    7. ],
    8. },
    9. })
    10. pets: Pet[];

    CatDog都应该使用@ApiExtraModels()装饰器 (在类水平).

    操作

    在OpenAPI规范中,API暴露的以{资源}为结束的终端,例如/users或者/reports/summary,都是可以执行HTTP方法的,例如GET,POST或者DELETE

    要为控制器附加一个标签,使用`@ApiTags(…tags)装饰器。

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

    报头

    要作为请求的一部分定义自定义报头,使用@ApiHeader()装饰器。

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

    响应

    要定义一个自定义响应, 使用`@ApiResponse()装饰器.

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

    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()
    1. @Post()
    2. @ApiCreatedResponse({ description: 'The record has been successfully created.'})
    3. @ApiForbiddenResponse({ description: 'Forbidden.'})
    4. async create(@Body() createCatDto: CreateCatDto) {
    5. this.catsService.create(createCatDto);
    6. }

    要从请求返回一个指定的模型,需要创建一个类并用@ApiProperty()装饰器注释它。

    1. export class Cat {
    2. @ApiProperty()
    3. id: number;
    4. @ApiProperty()
    5. name: string;
    6. @ApiProperty()
    7. age: number;
    8. @ApiProperty()
    9. breed: string;
    10. }

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

    1. @ApiTags('cats')
    2. @Controller('cats')
    3. export class CatsController {
    4. @Post()
    5. @ApiCreatedResponse({
    6. description: 'The record has been successfully created.',
    7. type: Cat,
    8. })
    9. async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
    10. return this.catsService.create(createCatDto);
    11. }
    12. }

    打开浏览器确认生成的Cat模型。

    OPENAPI - 图6

    文件上传

    使用@ApiBody装饰器和@ApiConsumes()来使能文件上传,这里有一个完整的使用技术的例子。

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

    FileUploadDto像这样定义:

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

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

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

    扩展

    要为请求增加一个扩展使用@ApiExtension()装饰器. 该扩展名称必须以 x-前缀。

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

    高级主题:通用ApiResponse

    基于原始定义,的能力,我们可以为Swagger定义通用原型:

    1. export class PaginatedDto<TData> {
    2. @ApiProperty()
    3. total: number;
    4. @ApiProperty()
    5. limit: number;
    6. @ApiProperty()
    7. offset: number;
    8. results: TData[];
    9. }

    我们跳过了定义results,因为后面要提供一个原始定义。现在,我们定义另一个DTO,例如CatDto如下:

    1. export class CatDto {
    2. @ApiProperty()
    3. name: string;
    4. @ApiProperty()
    5. age: number;
    6. @ApiProperty()
    7. breed: string;
    8. }

    我们可以定义一个PaginatedDto<CatDto>响应如下:

    1. @ApiOkResponse({
    2. schema: {
    3. allOf: [
    4. { $ref: getSchemaPath(PaginatedDto) },
    5. {
    6. properties: {
    7. results: {
    8. type: 'array',
    9. items: { $ref: getSchemaPath(CatDto) },
    10. },
    11. },
    12. },
    13. ],
    14. },
    15. })
    16. async findAll(): Promise<PaginatedDto<CatDto>> {}

    在这个例子中,我们指定响应拥有所有的PaginatedDto并且results属性类型为CatDto数组。

    • getSchemaPath() 函数从一个给定模型的OpenAPI指定文件返回OpenAPI原型路径
    • allOf是一个OAS3的概念,包括各种各样相关用例的继承。

    最后,因为PaginatedDto没有被任何控制器直接引用,SwaggerModule还不能生成一个相应的模型定义。我们需要一个,可以在控制器水平使用@ApiExtraModels()装饰器。

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

    如果你现在运行Swagger,为任何终端生成的swagger.json文件看上去应该像定义的这样:

    1. "responses": {
    2. "200": {
    3. "description": "",
    4. "content": {
    5. "application/json": {
    6. "schema": {
    7. "allOf": [
    8. {
    9. "$ref": "#/components/schemas/PaginatedDto"
    10. },
    11. {
    12. "properties": {
    13. "results": {
    14. "$ref": "#/components/schemas/CatDto"
    15. }
    16. }
    17. }
    18. ]
    19. }
    20. }
    21. }
    22. }
    23. }

    为了让其可重用,我们为PaginatedDto像这样创建一个装饰器:

    1. export const ApiPaginatedResponse = <TModel extends Type<any>>(
    2. model: TModel,
    3. ) => {
    4. return applyDecorators(
    5. ApiOkResponse({
    6. schema: {
    7. allOf: [
    8. { $ref: getSchemaPath(PaginatedDto) },
    9. {
    10. properties: {
    11. results: {
    12. type: 'array',
    13. items: { $ref: getSchemaPath(model) },
    14. },
    15. },
    16. },
    17. ],
    18. },
    19. }),
    20. );
    21. };

    Type<any>接口和applyDecorators函数从@nestjs/common引入.

    我们现在可以为终端使用自定义的@ApiPaginatedResponse()装饰器 :

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

    作为客户端生成工具,这一方法为客户端提供了一个含糊的PaginatedResponse<TModel>。下面示例展示了生成的客户端访问GET /终端的结果。

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

    可以看出,这里的返回类型是含糊不清的。要处理这个问题,可以为ApiPaginatedResponse原型添加title属性。

    1. export const ApiPaginatedResponse = <TModel extends Type<any>>(model: TModel) => {
    2. return applyDecorators(
    3. ApiOkResponse({
    4. schema: {
    5. title: `PaginatedResponseOf${model.name}`
    6. allOf: [
    7. // ...
    8. ],
    9. },
    10. }),
    11. );
    12. };

    现在结果变成了。

    1. // Angular
    2. findAll(): Observable<PaginatedResponseOfCatDto>

    要确定某个特定操作使用哪个安全机制,使用@ApiSecurity()装饰器。

    1. @ApiSecurity('basic')
    2. @Controller('cats')
    3. export class CatsController {}

    在运行程序前,使用DocumentBuilder在基础文档里添加安全定义。

    1. const options = new DocumentBuilder().addSecurity('basic', {
    2. type: 'http',
    3. scheme: 'basic',
    4. });

    一些最常用的认证机制是内置的(例如basicbearer),因此不需要像上面那样手动定义。

    Basic认证

    使用@ApiBasicAuth()配置basic认证。

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

    在运行程序前,使用DocumentBuilder在基础文档里添加安全定义。

    1. const options = new DocumentBuilder().addBasicAuth();

    Bearer认证

    使用@ApiBearerAuth()启用bearer认证。

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

    在运行程序前,使用DocumentBuilder在基础文档里添加安全定义。

    OAuth2认证

    使用@ApiOAuth2()启用OAuth2认证。

    1. @ApiOAuth2(['pets:write'])
    2. export class CatsController {}

    在运行程序前,使用DocumentBuilder在基础文档里添加安全定义。

    1. const options = new DocumentBuilder().addOAuth2();

    使用@ApiCookieAuth()启用cookie认证。

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

    在运行程序前,使用DocumentBuilder在基础文档里添加安全定义。

    1. const options = new DocumentBuilder().addCookieAuth('optional-session-id');

    映射的类型

    像构建CRUD特性一样,通常需要基于实体类型创建变体。Nest提供了一些应用函数来进行类型变换,以让这类变换工作更简单。

    Partial(部分声明)

    Nest提供了PartialType()应用函数让这一任务更简单地最小化构造。

    PartialType()函数返回一个类型(类)将输入的所有属性配置为可选的。例如,你可以这样创建一个类型。

    1. import { ApiProperty } from '@nestjs/swagger';
    2. export class CreateCatDto {
    3. @ApiProperty()
    4. name: string;
    5. @ApiProperty()
    6. age: number;
    7. @ApiProperty()
    8. breed: string;

    默认所有的字段都是必须的。要创建一个所有字段与之相同但都是可选的字段,使用PartialType()并将CreateCatDto作为参数。

    1. export class UpdateCatDto extends PartialType(CreateCatDto) {}

    PartialType()函数从@nestjs/swagger引入.

    Pick(拾取)

    PickType()函数从输入类型中拾取一部分属性并生成一个新类型(类) 。假设我们起始类如下:

    1. import { ApiProperty } from '@nestjs/swagger';
    2. export class CreateCatDto {
    3. @ApiProperty()
    4. name: string;
    5. @ApiProperty()
    6. age: number;
    7. @ApiProperty()
    8. breed: string;
    9. }

    我们使用PickType()从中拾取一部分属性:

    1. export class UpdateCatAgeDto extends PickType(CreateCatDto, ['age'] as const) {}

    PickType()函数 从@nestjs/swagger引入.

    OmitType()函数拾取所有输入属性,移除指定部分属性。例如,我们起始类型如下:

    1. import { ApiProperty } from '@nestjs/swagger';
    2. export class CreateCatDto {
    3. @ApiProperty()
    4. name: string;
    5. @ApiProperty()
    6. age: number;
    7. @ApiProperty()
    8. breed: string;
    9. }

    我们可以以此创建一个除name之外的包含其他所有属性的类。OmitType函数的第二个参数是包含要移除属性名称的数组。

    1. export class UpdateCatDto extends OmitType(CreateCatDto, ['name'] as const) {}

    OmitType()函数从@nestjs/swagger引入.

    Intersection(交叉)

    IntersectionType()函数将两个类型组合为一个类型(类),例如,我们起始的两个类型如下:

    1. import { ApiProperty } from '@nestjs/swagger';
    2. export class CreateCatDto {
    3. @ApiProperty()
    4. name: string;
    5. @ApiProperty()
    6. breed: string;
    7. }
    8. export class AdditionalCatInfo {
    9. @ApiProperty()
    10. color: string;
    11. }

    我们可以生成一个由两个类中所有属性组成的新类型。

    1. export class UpdateCatDto extends IntersectionType(
    2. CreateCatDto,
    3. AdditionalCatInfo,
    4. ) {}

    ?>IntersectionType()函数从@nestjs/swagger引入.

    Composition(组合)

    映射类型的使用时可以组合的,例如,以下代码创建一个类型(类),它包含了CreateCatDto除了name之外的所有属性,并将所有属性设置为可选的。

    1. export class UpdateCatDto extends PartialType(
    2. OmitType(CreateCatDto, ['name'] as const),
    3. ) {}

    装饰器

    所有可用的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

    TypeScript的元数据反射系统有一些限制,一些功能因此不可能实现,例如确定一个类由哪些属性组成,或者一个属性是可选的还是必须的。然而,一些限制可以在编译时强调。Nest提供了一个增强TypeScript编译过程的插件来减少需要的原型代码量。

    概述

    Swagger插件可以自动:

    • 使用@ApiProperty注释所有除了用@ApiHideProperty装饰的DTO属性。
    • 根据问号符号确定required属性(例如 name?: string 将设置required: false)
    • 根据类型配置typeenum(也支持数组)
    • 基于给定的默认值配置默认参数
    • 基于class-validator装饰器配置一些验证策略(如果classValidatorShim配置为true)
    • 为每个终端添加一个响应装饰器,包括合适的状态和类型(响应模式)
    • 根据注释生成属性和终端的描述(如果introspectComments配置为true)
    • 基于注释生成属性的示例数据(如果introspectComments配置为true)

    注意,你的文件名必须有如下后缀: ['.dto.ts', '.entity.ts'] (例如create-user.dto.ts) 才能被插件分析。

    如果使用其他后缀,你可以调整插件属性来指定dtoFileNameSuffix选项(见下文)。

    之前,如果你想要通过Swagger提供一个交互体验,你必须复制大量代码让包知道你的模型/组件在该声明中。例如,你可以定义一个CreateUserDto类:

    1. export class CreateUserDto {
    2. @ApiProperty()
    3. email: string;
    4. @ApiProperty()
    5. password: string;
    6. @ApiProperty({ enum: RoleEnum, default: [], isArray: true })
    7. roles: RoleEnum[] = [];
    8. @ApiProperty({ required: false, default: true })
    9. isEnabled?: boolean = true;
    10. }

    在中等项目中这还不是问题,但是一旦有大量类的话这就变得冗余而难以维护。

    要应用Swagger插件,可以简单声明上述类定义:

    1. export class CreateUserDto {
    2. email: string;
    3. password: string;
    4. roles: RoleEnum[] = [];
    5. isEnabled?: boolean = true;
    6. }

    插件可以通过抽象语法树添加合适的装饰器,你不在需要在代码中到处写ApiProperty装饰器。

    插件可以自动生成所有缺失的swagger属性,但是如果你要覆盖他们,只需要通过@ApiProperty()显式声明即可。

    注释自省

    注释自省特性使能后,CLI插件可以基于注释生成描述和示例值。

    例如,一个给定的roles属性示例:

    1. /**
    2. * A list of user's roles
    3. * @example ['admin']
    4. */
    5. @ApiProperty({
    6. description: `A list of user's roles`,
    7. example: ['admin'],
    8. })
    9. roles: RoleEnum[] = [];

    你必须复制描述和示例值。当introspectComments使能后,CLI插件可以自动解压这些注释并提供描述(以及示例,如果定义了的话)。现在,上述属性可以简化为:

    1. /**
    2. * A list of user's roles
    3. * @example ['admin']
    4. */
    5. roles: RoleEnum[] = [];

    使用CLI插件

    要使能CLI插件,打开nest-cli.json (如果你在用)并添加以下插件配置:

    1. {
    2. "collection": "@nestjs/schematics",
    3. "sourceRoot": "src",
    4. "compilerOptions": {
    5. "plugins": ["@nestjs/swagger"]
    6. }
    7. }

    你可以使用其他options属性来自定义插件特性。

    1. "plugins": [
    2. {
    3. "name": "@nestjs/swagger",
    4. "options": {
    5. "classValidatorShim": false,
    6. "introspectComments": true
    7. }
    8. }
    9. ]

    options属性实现以下接口:

    1. export interface PluginOptions {
    2. dtoFileNameSuffix?: string[];
    3. controllerFileNameSuffix?: string[];
    4. classValidatorShim?: boolean;
    5. introspectComments?: boolean;
    6. }
    选项 默认 说明
    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配合使用该插件:

    1. getCustomTransformers: (program: any) => ({
    2. before: [require('@nestjs/swagger/plugin').before({}, program)]
    3. }),

    ts-jest(e2e)

    要运行e2e测试,ts-jest在内存汇总编译源码,这意味着不使用Nest Cli编译,不应用任何插件或AST转换,要使用插件,在e2e测试目录下创建以下文件:

    1. const transformer = require('@nestjs/swagger/plugin');
    2. module.exports.name = 'nestjs-swagger-transformer';
    3. // you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
    4. module.exports.version = 1;
    5. module.exports.factory = (cs) => {
    6. return transformer.before(
    7. {
    8. // @nestjs/swagger/plugin options (can be empty)
    9. },
    10. cs.tsCompiler.program,
    11. );
    12. };

    在jest配置文件中引入AST变换。默认在(启动应用中),e2e测试配置文件在测试目录下,名为jest-e2e.json

    1. {
    2. ... // other configuration
    3. "globals": {
    4. "ts-jest": {
    5. "astTransformers": {
    6. "before": ["<path to the file created above>"],
    7. }
    8. }
    9. }
    10. }

    其他特性

    全局前缀

    要忽略一个通过setGlobalPrefix()配置的全局前缀, 使用ignoreGlobalPrefix:

    1. const document = SwaggerModule.createDocument(app, options, {
    2. ignoreGlobalPrefix: true,
    3. });

    多重声明

    Swagger模块提供了一个支持多重声明的方法,也就是说可以在多个终端提供多个界面和多个文档。

    要支持多重声明,首先在模块中要进行声明,在createDocument()方法中传递第3个参数,extraOptions,这是个包含一个叫做include名称的属性,该属性提供了一个由模块组成的数组。

    可以如下配置多重声明:

    1. import { NestFactory } from '@nestjs/core';
    2. import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
    3. import { AppModule } from './app.module';
    4. async function bootstrap() {
    5. const app = await NestFactory.create(AppModule);
    6. /**
    7. * createDocument(application, configurationOptions, extraOptions);
    8. *
    9. * createDocument method takes an optional 3rd argument "extraOptions"
    10. * which is an object with "include" property where you can pass an Array
    11. * of Modules that you want to include in that Swagger Specification
    12. * E.g: CatsModule and DogsModule will have two separate Swagger Specifications which
    13. * will be exposed on two different SwaggerUI with two different endpoints.
    14. */
    15. const options = new DocumentBuilder()
    16. .setTitle('Cats example')
    17. .setDescription('The cats API description')
    18. .setVersion('1.0')
    19. .addTag('cats')
    20. .build();
    21. const catDocument = SwaggerModule.createDocument(app, options, {
    22. include: [CatsModule],
    23. });
    24. SwaggerModule.setup('api/cats', app, catDocument);
    25. const secondOptions = new DocumentBuilder()
    26. .setTitle('Dogs example')
    27. .setDescription('The dogs API description')
    28. .setVersion('1.0')
    29. .addTag('dogs')
    30. .build();
    31. const dogDocument = SwaggerModule.createDocument(app, secondOptions, {
    32. include: [DogsModule],
    33. });
    34. SwaggerModule.setup('api/dogs', app, dogDocument);
    35. await app.listen(3000);
    36. }
    37. bootstrap();

    现在可以使用以下命令启动服务器:

    1. $ npm run start

    访问http://localhost:3000/api/cats可以看到catsSwagger界面, 访问http://localhost:3000/api/dogs可以看到dogsSwagger界面。

    迁移指南

    如果你在使用@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

    译者署名

    | 用户名 | 头像 | 职能 | 签名 | |—-|—-|—-|—-|