2020-01-11 • admin • NestJS入门swagger,快速搭建restfulApi文档已关闭评论条评论 • 550 次浏览
*由于最近新版@nestjs/swagger4.*的更新,使用的注解也发生了一些改动,具体可以查看
@nestjs/swagger官方地址
swagger:一个功能强大的高清格式来描述RESTful API
。Nest提供了专用的模块来使用它
yarn add @nestjs/swagger swagger-ui-express --save
如果使用fastify,则必须安装fastify-swagger而不是swagger-ui-express:
yarn add @nestjs/swagger fastify-swagger --save
// main.ts 中配置
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
// api文档插件
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
const app = await NestFactory.create(AppModule);
// DocumentBuilder是一个辅助类,有助于结构的基本文件SwaggerModule。它包含几种方法,可用于设置诸如标题,描述,版本等属性。
const options = new DocumentBuilder()
.setTitle('nest入门接口标题')
.setDescription('使用nest书写的常用性接口') // 文档介绍
.setVersion('1.0.0') // 文档版本
.addTag('用户,安全') // 每个tag标签都可以对应着几个@ApiUseTags('用户,安全') 然后被ApiUseTags注释,字符串一致的都会变成同一个标签下的
// .setBasePath('http://localhost:5000')
.build();
// 为了创建完整的文档(具有定义的HTTP路由),我们使用类的createDocument()方法SwaggerModule。此方法带有两个参数,分别是应用程序实例和基本Swagger选项。
const document = SwaggerModule.createDocument(app, options);
// 最后一步是setup()。它依次接受(1)装入Swagger的路径,(2)应用程序实例, (3)描述Nest应用程序的文档。
SwaggerModule.setup('/api', app, document);
await app.listen(5000);
1.先通过DocumentBuilder实例来设置文档的配置选项,例如版本、标题、文档介绍、多个标签等
2.然后通过@nestjs/swagger模块提供的SwaggerModule的createDocument方法创建文档,传递整个app(应用程序实例)为第一个参数,第二个参数就是1配置号的文档选项
3.第三步是通过SwaggerModule的setup方法出口创建文档的url,它依次接受(1)装入Swagger的路径,(2)应用程序实例, (3)描述Nest应用程序的文档。
这时候会变成默认的配置文档选项
这时候启动默认初始化的项目,访问http://localhost:3000/api/
然后返回正确的状态和数据,文档都无需自己手写,减少不少的文档编辑量定义控制器时,SwaggerModule寻找所有的使用@Body(),@Query()以及@Param()在路由处理器装饰。因此,可以创建有效的文档。
2.1 我们创建user文件夹,存放user相关的module,controller,service,代码如下:
// user.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {
public getUser(id: string): string {
return `用户的id:${id}`;
}
}
// user.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('/user')
export class UserController {
constructor(private userService: UserService) { }
@Get('/get/:id')
public getUser(@Param('id') id: string): string {
return this.userService.getUser(id);
}
}
// user.module.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
providers: [UserService],
controllers: [UserController],
})
export class UserModule { }
// user.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from './user.service';
describe('UserController', () => {
let userController: UserController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [UserController],
providers: [UserService],
}).compile();
userController = app.get(UserController);
});
describe('user', () => {
it('should return "用户的id: xxx"', () => {
expect(userController.getUser('111')).toBe('用户的id:111');
});
});
});
这时候我们看一下swagger的文档,发现多了/user/get/{id}这个get请求的路由。
当然,这样往往是不足够的,swagger还提供了修饰dto、参数、请求响应等配置
swagger的配置装饰器都是以@api开头
3.1 ApiTags装饰器,让对应的模块分类到对应的标签当中
在user.controller.ts中添加该装饰器在控制器类上
import { ApiTags } from '@nestjs/swagger';
import { Controller, Get, Query } from '@nestjs/common';
@ApiTags('用户,安全')
@Controller('/user')
export class UserController {
//...
}
然后对应的这个控制器就分配到该组
3.2 ApiQuery、ApiBody、ApiParam、ApiHeader、ApiHeaders
除了ApiImplicitHeaders之外,其它的接收一个对象,对象类型如下:
name: string; // 该数据的名称,比如:id可以写用户id或者id
description?: string; // 简介
required?: boolean; // 是否是必须的
type?: any; // 类型
isArray?: boolean; // 是否是数组
enum?: SwaggerEnumType; // 枚举类型
collectionFormat?: "csv" | "ssv" | "tsv" | "pipes" | "multi";
而ApiHeaders需要的对象只有三个参数
name: string;
description?: string;
required?: boolean;
修改user.controller.ts文件成如下代码:
import { Controller, Get, Param, Query } from '@nestjs/common';
import { ApiTags, ApiParam, ApiQuery, ApiHeader } from '@nestjs/swagger';
import { UserService } from './user.service';
@ApiTags('用户,安全')
@Controller('/user')
export class UserController {
constructor(private userService: UserService) { }
@Get('/get/:id')
@ApiParam({
name: 'id',
description: '这是用户id',
})
@ApiQuery({
name: 'role',
description: '这是需要传递的参数',
})
@ApiHeader({
name: 'authoriation',
required: true,
description: '本次请求请带上token',
})
public getUser(@Param('id') id: string, @Query('role') role: string): string {
return this.userService.getUser(id);
}
}
保存,刷新页面
会发现变成,对应的字段有对应的描述信息
我们只需要在编写接口的同时添加swagger提供装饰器即可,无需开发过后再回来编写文档
3.3 还有就是dto的参数配置ApiProperty
// user.controller.ts 加上如下方法
@Post('/add')
public addUser(@Body() user: User) {
return user;
}
创建一个User对象
// User.ts
import { ApiProperty } from '@nestjs/swagger';
export class User {
@ApiProperty({
description: '用户名',
})
username: string;
@ApiProperty({
description: '密码',
})
password: string;
}
这时候的文档
ApiProperty可以接受的对象配置参数有许多,具体可以参考官方
https://docs.nestjs.com/recipes/swagger
当参数是数组的情况下,我们可以这样配置@ApiProperty({ type: [String] })
3.4 还有就是ApiResponse,用来装饰方法
@ApiResponse({ status: 401, description: '权限不足'})
@Post('/add')
public addUser(@Body() user: User) {
return user;
}
nestjs还内置了大量的相关http状态码的描述,具体可以参照官方
https://docs.nestjs.com/recipes/swagger
3.5 ApiImplicitFile 可以用于文件上传的文档测试
例如在addUser方法加上该装饰器
@ApiResponse({ status: 401, description: '权限不足'})
@ApiImplicitFile({
name: '头像',
description: '上传头像',
required: false,
})
@Post('/add')
public addUser(@Body() user: User) {
return user;
}
这时候,我们可以看见文档就是这样的:
具体使用还需要结合实际,以上全部装饰器都来自@nestjs/swagger
如果需要继续深入,可以观看官方文档的案例
修改,把上面的文档拆分成两个文档
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { UserModule } from './user/user.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.setTitle('用户信息文档')
.setDescription('用于用户信息的增删改查')
.setVersion('1.0')
.addTag('用户,安全')
.build();
const userDocument = SwaggerModule.createDocument(app, options, {
include: [UserModule], // 包含的模块
});
SwaggerModule.setup('api/user', app, userDocument);
const secondOptions = new DocumentBuilder()
.setTitle('整体文档')
.setDescription('包含了测试文档和前台应用文档')
.setVersion('1.0')
.addTag('用户,安全')
.build();
const appDocument = SwaggerModule.createDocument(app, secondOptions, {
include: [AppModule, UserModule],
});
SwaggerModule.setup('api', app, appDocument);
await app.listen(3000);
}
bootstrap();
这时候,我们的文档就分成了 http://localhost:3000/api 这个整体文档和 用户模块的文档 http://localhost:3000/api/user
我们发现http://localhost:3000/api和前面的一致,而http://localhost:3000/api/user只有用户模块的文档