一句話概括:基於springboot+swagger實現接口文檔顯示後,本文將給出在企業實踐的進階需求,包括接口按需過濾,前端mock數據,文檔離線導出。
1.引言
在上一篇文章《springboot+swagger接口文檔企業實踐(上)》已對使用springboot+swagger的接口文檔構建及配置進行了介紹,可以實時顯示接口的輸入輸出,還可以調用接口調試。解決了後端開發人員寫接口文檔的難處。但在企業實踐中,還有一些問題需要解決,如以下幾種:
- 發佈指定的接口:不希望把全部的接口都對外發布提供,只需要發佈指定的接口。
- 發佈指定版本接口:每一次版本迭代,只需要對變更和新增的接口進行說明,而不是每次都輸出全部的接口(每次都全量發佈接口,前端人員還得與後端溝通哪些是變更和新增接口,無疑會增加工作量)
- mock接口數據:有了接口文檔,前後端可以並行開發,此時前端需要模擬接口的返回數據來顯示效果,測試內容等。
- 離線導出接口文檔:當前的接口文檔是通過頁面訪問的,但有些企業(特別注重文檔交付的企業)還是有一些特殊需求,需要離線的接口文檔。
針對以上的情況,本文提供相應的解決方法,主要包含以下內容:
- 接口過濾:通過包過濾、類註解過濾、方法註解過濾、分組過濾等方式,實現按需發佈指定接口的功能。
- 使用easy-mock+swagger實現mock數據。
- 使用maven插件實現接口文檔的離線導出。
本文配套的示例工程地址:https://github.com/mianshenglee/my-example/tree/master/springboot-swagger-demo/advance-swagger-demo
,讀者可fork或pull下來,結合學習。
2. swagger接口過濾
對於swagger的接口文檔,在實踐開發中,一般只需要發佈指定的接口,按需發佈即可,針對版本迭代,只需要對變更和新增的接口進行說明,而不是每次都輸出全部的接口。這些需求都屬於swagger的接口過濾功能。在springboot的swagger配置類(Swagger2Config.java
)中,Docket提供了apis()
和paths()
兩個方法用於接口過濾,下面詳細說明一下。
2.1 按包過濾(package)
一般接口都是寫在控制器(controller)中,而controller一般都統一放在一個package中,這樣,可以通過包過濾顯示指定包的接口。通過在配置文件Swagger2Config.java
中使用apis()
函數進行過濾,把需要顯示的接口使用函數進行返回,注意,此處的參數enableClassFilter
,enableMethodFilter
和groupsFilters
分別對應後面的過濾方法,定義如下:
private List<Predicate<RequestHandler>> apisFilter(boolean enableClassFilter,boolean enableMethodFilter, String[]groupsFilters) {
List<Predicate<RequestHandler>> apis = new ArrayList<>();
String basePackageStr = swaggerInfo.getBasePackage();
// 1.包過濾
if (StrUtil.isNotEmpty(basePackageStr)) {
//支持多個包
String[] basePackages = basePackageStr.split(";");
if (null != basePackages && basePackages.length > 0) {
Predicate<RequestHandler> predicate = input -> {
// 按basePackage過濾
Class<?> declaringClass = input.declaringClass();
String packageName = declaringClass.getPackage().getName();
return Arrays.asList(basePackages).contains(packageName);
};
apis.add(predicate);
}
}
return apis;
}
此處代碼作用是按配置項swagger.basePackage
的包(多個包用;
分隔),從接口所在類中獲取包名,若包名是在配置的包內,則返回,並把匹配的內容使用List
返回。然後在apis()
調用時對返回值進行and操作Predicates.and(apisFilter())
,這樣,返回的內容就只是指定的包的接口描述。
2.2 按類註解過濾
接口一般是在Controller類中,對於springmvc的controller,都會使用@Controller
進行註解,甚至前後端分離一般都是使用@RestController
,因此,如果我們想只顯示使用@RestController
註解的類的接口,則可以對此進行過濾。在前面的apisFilter()
方法中。使用isAnnotationPresent
可判斷是否有某註解,如下所示
// 2.過濾被RestController註解的類
if(enableClassFilter){
Predicate<RequestHandler> predicate = input -> {
Class<?> declaringClass = input.declaringClass();
return declaringClass.isAnnotationPresent(RestController.class);
};
apis.add(predicate);
}
2.3 按方法註解過濾
按類註解是把整個類過濾掉,粒度較大,如果只想按方法過濾,可以使用swagger的@ApiIgnore
註解對接口進行過濾,有此註解則不顯示。也可以通過判斷是否存在某個指定方法註解來過濾。swagger的接口描述一般都用@ApiOperation
,因此,可以通過判斷接口是否存在此註解,如果沒有則不顯示。如下:
// 3.過濾被ApiOperation註解的方法
if(enableMethodFilter){
apis.add(input -> input.isAnnotatedWith(ApiOperation.class));
}
2.4 按分組過濾
swagger的接口文檔如果沒有指定groupName,則會默認以default
作爲分組名,對應的接口文檔是v2/api-docs?group=default
,它會按apis
過濾後的全部接口顯示出來。在迭代版本時,有個顯示的需求是只需要顯示當前版本變更和新增的接口。其實也是使用註解過濾的方式,結合groupName進行設置即可。具體如下:
2.4.1 定義註解ApiVersion
此註解需自定義,用於指定版本號,注意可以是多個版本(多版本兼容的情況)。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiVersion {
/**
* 接口版本號(對應swagger中的group)
* @return String[]
*/
String[] group();
}
當開發新的版本,在變更和新增的接口中添加此註解,並把版本號寫到對應的group中即可,如
@ApiVersion(group = {"v1.0.0"})
2.4.2 添加ApiVersion
過濾
在apisFilter()
函數中,添加以下過濾代碼:
// 4.過濾組
if(groupsFilters !=null && groupsFilters.length >0){
Predicate<RequestHandler> predicate = input -> {
ApiVersion apiVersion = input.getHandlerMethod().getMethodAnnotation(ApiVersion.class);
return apiVersion!=null && ArrayUtil.containsAny(apiVersion.group(),groupsFilters);
};
apis.add(predicate);
}
從接口方法中獲取ApiVersion
註解,並獲取它的group
參數,通過與指定的分組進行比較,存在即顯示,否則不顯示。
2.4.3 新建指定版本號分組的Docket
添加新的一個Docket Bean,指定groupName
爲當前需要顯示的版本號,並輸入需要過濾的分組數組。如下:
@Bean
public Docket v100Api() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName(ApiVersionConstant.VERVION_100)
...//略
ApiSelectorBuilder builder = docket.select();
//指定需要過濾的版本號
builder = builder.apis(Predicates.and(apisFilter(false,true,new String[]{ApiVersionConstant.VERVION_100})));
...//略
return builder.build();
}
經過上面的處理,顯示的接口界面有兩個分組,一個是default,一個是指定的版本號(v1.0.0),指定版本顯示的接口即是當前版本變更或新增的接口描述,如下:
3. 接口mock數據
有了接口文檔,前後端可以並行開發,此時前端需要模擬接口的返回數據來顯示效果,測試內容。雖然swagger提供example
屬性,可以在返回結果中提供示例,但對於前端而言,單一的示例不足以滿足顯示和測試的需求。需要對數據按接口情況進行mock。easy-mock是一個很好的選擇,它可以連接swagger,自動生成mock數據,也可以自定義mock規則。下面對easy-mock+swagger的使用進行描述。
3.1 easy-mock安裝
根據easy-mock的官網介紹(官網經常掛掉,建議直接使用它的github私有部署), Easy Mock 是一個可視化,並且能快速生成模擬數據的持久化服務。 可以使用它的在線服務,也可以私有部署。它是開源項目,github地址:https://github.com/easy-mock/easy-mock
,具有以下特徵:
它的官方文檔和github文檔中,已經對easymock的安裝和使用進行詳細描述,讀者可參考官方文檔,此處只列出安裝的關鍵點和需要注意的地方(本文使用的easy-mock及相關工具是在centos7上安裝的)。
- 安裝easy-mock前,需先安裝了 Node.js(v8.x)、 MongoDB(>= v3.4)、 Redis(>= v4.0) ,且已正常運行。關於這幾個軟件的安裝與運行,請讀者自行查看官網教程。
- 安裝 easy-mock。
$ git clone https://github.com/easy-mock/easy-mock.git
$ cd easy-mock && npm install
- 更改 easy-mock\config 文件夾下的配置文件 default.json,注意修改host爲"0.0.0.0",修改連接mongodb的地址以及redis的host,端口等信息。
- 啓動 easy-mock,這種不是在後臺運行的方式,直接
ctrl+c
可關閉。
$ npm run dev
# 啓動後訪問 http://127.0.0.1:7300
- 使用pm2進行後臺啓動, 啓動成功後,在命令行中輸入
netstat -ntlp
查看正在使用的端口(如mongodb的27017,redis的6379,easy-mock的7300等)
$ [sudo] npm install pm2 -g
$ pm2 start app.js
easy-mock啓動後,通過瀏覽器可以訪問, 輸入用戶名和密碼(如果用戶不存在會自動註冊)。
3.2 easy-mock + swagger實現mock數據
easy-mock是使用項目來進行接口管理,可以創建個人項目,也可以加入到其它人創建的項目,項目即是需要mock的接口。
3.2.1 創建項目並添加swagger地址
在創建項目時,可以設置swagger的接口文檔地址,以此導入swagger的接口進行管理與模擬。前面提到,使用分組過濾可以按版本號來顯示接口,因此,創建項目時,可以使用版本號作爲項目名稱,一個版本對應一個項目。這樣,前端在mock數據時就可以針對當前版本進行處理。如下爲示例項目中的v1.0.0版本(填寫的swagger接口文檔地址爲v2/api-docs?group=v1.0.0
):
填寫swagger地址後,會自動導入對應的接口,與前端看到swaggger-ui.html
的接口一致,執行測試時,也會按swgger的返回數據類型或example進行返回。
3.2.2 自定義模擬數據
模擬數據使用的是 Mock.js ,在其官網中有相應的文檔、示例和代碼。讀者可以上去詳細閱讀。簡單來說,mockjs提供了對String
,Number
,Boolean
,Object
,Array
等數據的模擬規則,只需按規則編寫,即可生成隨機數據,達到模擬數據效果。生成規則有 7 種格式:
'name|min-max': value
'name|count': value
'name|min-max.dmin-dmax': value
'name|min-max.dcount': value
'name|count.dmin-dmax': value
'name|count.dcount': value
'name|+step': value
使用了自定義的模擬數據後,需要注意以下幾點:
- 使用了自定義的模擬數據規則後,前端在使用此接口進行模擬數據時,原來在swagger中定義的示例和返回值就會被自定義的數據覆蓋。
- 當後端接口有變化,需要前端在easy-mock中重新同步新的swagger接口,同步前需要備份原來的自定義數據規則,否則,重新同步後,原來自定義的數據會swagger的接口定義覆蓋。因此,最好是先備份,然後針對有改動的地方進行規則修改即可。
4.接口文檔離線導出
一般在開發過程中使用swagger文檔,直接使用瀏覽器訪問swagger-ui.html
頁面即可滿足要求,若有需求是需要導出離線接口文檔,總體可以按以下思路進行:
- 使用
swagger
的v2/api-docs
的url地址導出json文檔(不導出也可以直接使用此url作爲輸入文檔的地址) - 使用
swagger2markup
工具將json文檔轉爲asciidoc格式文檔。 - 使用
asciidoctor
工具將asciidoc文件轉html或pdf文件。
4.1 導出swagger的json文檔
在瀏覽器中訪問接口文檔頁面的地址是/swagger-ui.html
,而swagger的規範文檔的地址是/v2/api-docs
,ctrl+s
把此文件保存爲json文件,如api-docs.json
。在項目的根目錄添加一個docs
目錄,用於存放離線文檔相關內容,如下所示,分別建立對應文檔格式的目錄,並把api-docs.json
放在swagger目錄下:
├─asciidoc
├─html
├─pdf
└─swagger
└─api-docs.json
4.2 導出asciidoc文檔
有了api-docs.json
文檔,即可使用swagger2markup導出asciidoc文檔,Swagger2Markup是Github上的一個開源項目。該項目主要用來將Swagger自動生成的文檔轉換成幾種流行的格式以便於靜態部署和使用,比如:AsciiDoc、Markdown、Confluence。使用swagger2markup導出asciidoc文檔有兩種方式:
- 引入swagger2markup的jar包,通過編碼方式生成對應格式的文檔輸出。
- 使用swagger2markup的maven插件,以mvn命令生成對應格式文檔輸出。
第一種方式示例代碼中有提供SwaggerExportTest.java
,其中編寫了相應的測試代碼用於生成文檔,請讀者自行閱讀。本文主要使用第二種方式,即maven插件進行asciidoc文檔輸出。
4.2.1 設置文檔輸出相關插件版本
以下插件版本在示例可正常運行,更高的版本有可能會出現不兼容的情況,因此請讀者按本文設置的版本進行處理。文檔輸出的路徑以前面指定的相關docs目錄爲準(${basedir}
是項目的根目錄)。
<!-- 文檔輸出插件版本 -->
<swagger.plugin.version>3.1.8</swagger.plugin.version>
<swagger2markup.version>1.3.1</swagger2markup.version>
<swagger2markup.plugin.version>1.3.3</swagger2markup.plugin.version>
<asciidoctor.plugin.version>1.5.7</asciidoctor.plugin.version>
<!-- 文檔輸出路徑 -->
<docs.path>${basedir}/docs</docs.path>
<docs.asciidoc.path>${docs.path}/asciidoc</docs.asciidoc.path>
<docs.html.path>${docs.path}/html</docs.html.path>
<docs.pdf.path>${docs.path}/pdf</docs.pdf.path>
<docs.swagger.json.path>${docs.path}/swagger/api-docs.json</docs.swagger.json.path>
4.2.2 添加swagger2markup-maven-plugin
插件
在build/plugins
元素下,添加以下元素:
<!-- 1.swagger.json文件轉asciidoc文件-->
<plugin>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup-maven-plugin</artifactId>
<version>${swagger2markup.plugin.version}</version>
<configuration>
<!-- 訪問url -->
<!--<swaggerInput>http://localhost:8080/swaggerdemo/v2/api-docs?group=default</swaggerInput>-->
<!-- 訪問json文件-->
<swaggerInput>${docs.swagger.json.path}</swaggerInput>
<!-- 生成多個文檔輸出路徑 -->
<!--<outputDir>${docs.asciidoc.path}</outputDir>-->
<!-- 生成單個文檔輸出路徑 -->
<outputFile>${docs.asciidoc.path}/all</outputFile>
<config>
<swagger2markup.pathsGroupedBy>TAGS</swagger2markup.pathsGroupedBy>
<!-- 選擇:ASCIIDOC/MARKDOWN/CONFLUENCE_MARKUP-->
<swagger2markup.markupLanguage>ASCIIDOC</swagger2markup.markupLanguage>
</config>
</configuration>
</plugin>
說明:
- swaggerInput元素可以是導出的json文件路徑,也可以使用訪問url地址(
v2/api-docs
),效果是一樣的。但若使用url,則需要確保先把應用啓動,能正常訪問url。 - outputFile元素是指把全部內容輸出到一個文件中,若使用outputDir,則會把各種類型的內容,分開成多個文件輸出。
- swagger2markup.markupLanguage元素可以選擇導出
ASCIIDOC/MARKDOWN/CONFLUENCE_MARKUP
三種格式的文檔,此處使用ASCIIDOC
即可。
4.2.3 使用命令輸出文檔
添加完此插件後,使用mvn swagger2markup:convertSwagger2markup
命令即可以導出文件到指定的目錄。如本示例中的輸出是docs/asciidoc/all.adoc
4.3 導出html/pdf文檔
導出asciidoc文檔後,使用asciidoctor插件對其轉換爲html和pdf文檔輸出。
4.3.1 添加asciidoctor插件
如下所示,通過添加asciidoctor-maven-plugin,並對它進行配置:
<!-- 2.asciidoc文件轉html/pdf文件-->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>${asciidoctor.plugin.version}</version>
<!-- 轉換pdf使用的依賴 -->
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>1.5.0-alpha.16</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>9.2.8.0</version>
</dependency>
</dependencies>
<configuration>
<sourceDirectory>${docs.asciidoc.path}</sourceDirectory>
<doctype>book</doctype>
<sourceHighlighter>coderay</sourceHighlighter>
<headerFooter>true</headerFooter>
<attributes>
<!-- 菜單欄在左邊 -->
<toc>left</toc>
<!-- 三級目錄 -->
<toclevels>3</toclevels>
<!-- 數字序號 -->
<sectnums>true</sectnums>
</attributes>
</configuration>
<!-- 生成html和pdf兩種格式 -->
<executions>
<execution>
<id>output-html</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<outputDirectory>${docs.html.path}</outputDirectory>
<backend>html</backend>
</configuration>
</execution>
<execution>
<id>output-pdf</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<outputDirectory>${docs.pdf.path}</outputDirectory>
<backend>pdf</backend>
<!-- 處理中文字符問題 -->
<attributes>
<pdf-fontsdir>${docs.pdf.path}/fonts</pdf-fontsdir>
<pdf-stylesdir>${docs.pdf.path}/themes</pdf-stylesdir>
<pdf-style>cn</pdf-style>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
此配置相對較長,其實主要分爲三部分:
- 轉換pdf使用的依賴,即
<dependencies>
元素,直接使用即可。 - 通用文檔配置
<configuration>
元素,其中需要注意的是<sourceDirectory>
,此處需要配置上一個步驟生成的asiidoc
文件目錄路徑。 <executions>
元素:由於需要生成html和pdf兩種格式的文檔,因此分別使用executions
來實現。其中html相對簡單,配置outputDirectory
和backend
元素指定輸出目錄路徑和文件格式html即可,對應的pdf也一樣配置。
對於pdf文檔的轉換,需要解決中文字體缺失的問題。
- 若不配置對應的中文字符支持(即配置中的
<attributes>
元素),則會出現中文亂碼或文字缺失的情況,如下圖,缺失了戶
,對
這幾個字:
- pdf中文字符支持,在github中,已有對asciidoctor-pdf中文字體的支持,在下載頁面下載
KaiGenGothicCN
開頭和RobotoMono
開頭的ttf字體,同時下載cn-theme.yml
文件,分別放到docs/pdf
目錄下:
├─pdf
│ ├─fonts
│ │ ├─KaiGenGothicCN-Bold-Italic.ttf
│ │ ├─KaiGenGothicCN-Bold.ttf
│ │ ├─KaiGenGothicCN-Regular-Italic.ttf
│ │ ├─KaiGenGothicCN-Regular.ttf
│ │ ├─RobotoMono-Bold.ttf
│ │ ├─RobotoMono-BoldItalic.ttf
│ │ ├─RobotoMono-Italic.ttf
│ │ └─RobotoMono-Regular.ttf
│ └─themes
│ └─cn-theme.yml
- 使用
<attributes>
元素設置對應的pdf-fontsdir
,pdf-stylesdir
及pdf-style
,指定下載好的fonts目錄和themes目錄。
4.3.2 使用命令輸出文檔
使用命令mvn generate-resources
,即可生成對應的html和pdf文檔到對應的目錄,分別是docs/html/all.html
及docs/html/all.pdf
。效果如下圖所示:
- html文檔
- pdf文檔(缺失文字問題已解決)
5. 總結
本篇文章針對接口過濾顯示,前端mock數據和離線導出文檔等問題,提供相應的解決方法,包括接口過濾(包過濾、類註解過濾、方法註解過濾、分組過濾等方式),實現按需發佈指定接口的功能;使用easy-mock+swagger實現mock數據;使用maven插件實現接口文檔的離線導出。希望對有需要的同學有所幫助。本文配套的示例工程地址:https://github.com/mianshenglee/my-example/tree/master/springboot-swagger-demo/advance-swagger-demo
,讀者可fork或pull下來,結合學習。
相關閱讀
參考資料
- swagger2markup-maven-plugin:
https://github.com/Swagger2Markup/swagger2markup-maven-plugin
- asciidoctor-maven-plugin:
https://asciidoctor.org/docs/asciidoctor-maven-plugin/
asciidoctor-pdf中文字體:
https://github.com/chloerei/asciidoctor-pdf-cjk-kai_gen_gothic/releases
使用Swagger2Markup、asciidoctor-maven-plugin和asciidoctorj-pdf插件生成PDF格式的API文檔中文問題解決:
https://blog.csdn.net/lihuaijun/article/details/79727863
關注我的公衆號,獲取更多技術記錄: