swagger
*由于最近新版@nestjs/swagger4.*的更新,使用的注解也發生了一些改動,具體可以檢視
*由于最近新版@nestjs/swagger4.*的更新,使用的注解也發生了一些改動,具體可以檢視
@nestjs/swagger官方位址
swagger:一個功能強大的高清格式來描述 RESTful API
。Nest提供了專用的子產品來使用它
RESTful API
1. 安裝swagger
yarn add @nestjs/swagger swagger-ui-express --save
如果使用fastify,則必須安裝fastify-swagger而不是swagger-ui-express:
yarn add @nestjs/swagger fastify-swagger --save
2.配置文檔格式資訊
// 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<NestExpressApplication>(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>(UserController);
});
describe('user', () => {
it('should return "使用者的id: xxx"', () => {
expect(userController.getUser('111')).toBe('使用者的id:111');
});
});
});
這時候我們看一下swagger的文檔,發現多了/user/get/{id}這個get請求的路由。
當然,這樣往往是不足夠的,swagger還提供了修飾dto、參數、請求響應等配置
3.swagger的配置裝飾器
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檔案成如下代碼:
修改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
如果需要繼續深入,可以觀看官方文檔的案例
4. 多個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隻有使用者子產品的文檔