springboot+swagger接口文檔企業實踐(下)

tags: springboot swagger dochtml


一句話歸納:基於springboot+swagger實現接口文檔顯示後,本文將給出在企業實踐的進階需求,包括接口按需過濾,前端mock數據,文檔離線導出。前端

1.引言

在上一篇文章《springboot+swagger接口文檔企業實踐(上)》已對使用springboot+swagger的接口文檔構建及配置進行了介紹,能夠實時顯示接口的輸入輸出,還能夠調用接口調試。解決了後端開發人員寫接口文檔的難處。但在企業實踐中,還有一些問題須要解決,如如下幾種:java

  • 發佈指定的接口:不但願把所有的接口都對外發布提供,只須要發佈指定的接口。
  • 發佈指定版本接口:每一次版本迭代,只須要對變動和新增的接口進行說明,而不是每次都輸出所有的接口(每次都全量發佈接口,前端人員還得與後端溝通哪些是變動和新增接口,無疑會增長工做量)
  • mock接口數據:有了接口文檔,先後端能夠並行開發,此時前端須要模擬接口的返回數據來顯示效果,測試內容等。
  • 離線導出接口文檔:當前的接口文檔是經過頁面訪問的,但有些企業(特別注重文檔交付的企業)仍是有一些特殊需求,須要離線的接口文檔。

針對以上的狀況,本文提供相應的解決方法,主要包含如下內容:node

  1. 接口過濾:經過包過濾、類註解過濾、方法註解過濾、分組過濾等方式,實現按需發佈指定接口的功能。
  2. 使用easy-mock+swagger實現mock數據。
  3. 使用maven插件實現接口文檔的離線導出。

本文配套的示例工程地址https://github.com/mianshenglee/my-example/tree/master/springboot-swagger-demo/advance-swagger-demo,讀者可fork或pull下來,結合學習。git

2. swagger接口過濾

對於swagger的接口文檔,在實踐開發中,通常只須要發佈指定的接口,按需發佈便可,針對版本迭代,只須要對變動和新增的接口進行說明,而不是每次都輸出所有的接口。這些需求都屬於swagger的接口過濾功能。在springboot的swagger配置類(Swagger2Config.java)中,Docket提供了apis()paths()兩個方法用於接口過濾,下面詳細說明一下。github

2.1 按包過濾(package)

通常接口都是寫在控制器(controller)中,而controller通常都統一放在一個package中,這樣,能夠經過包過濾顯示指定包的接口。經過在配置文件Swagger2Config.java中使用apis()函數進行過濾,把須要顯示的接口使用函數進行返回,注意,此處的參數enableClassFilterenableMethodFiltergroupsFilters分別對應後面的過濾方法,定義以下:redis

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()),這樣,返回的內容就只是指定的包的接口描述。spring

2.2 按類註解過濾

接口通常是在Controller類中,對於springmvc的controller,都會使用@Controller進行註解,甚至先後端分離通常都是使用@RestController,所以,若是咱們想只顯示使用@RestController註解的類的接口,則能夠對此進行過濾。在前面的apisFilter()方法中。使用isAnnotationPresent可判斷是否有某註解,以下所示mongodb

// 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,所以,能夠經過判斷接口是否存在此註解,若是沒有則不顯示。以下:shell

// 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,具備如下特徵:

easy-mock特徵

它的官方文檔github文檔中,已經對easymock的安裝和使用進行詳細描述,讀者可參考官方文檔,此處只列出安裝的關鍵點和須要注意的地方(本文使用的easy-mock及相關工具是在centos7上安裝的)。

  1. 安裝easy-mock前,需先安裝了 Node.jsv8.x)、 MongoDB>= v3.4)、 Redis>= v4.0) ,且已正常運行。關於這幾個軟件的安裝與運行,請讀者自行查看官網教程。
  2. 安裝 easy-mock。
$ git clone https://github.com/easy-mock/easy-mock.git
$ cd easy-mock && npm install
複製代碼
  1. 更改 easy-mock\config 文件夾下的配置文件 default.json,注意修改host爲"0.0.0.0",修改鏈接mongodb的地址以及redis的host,端口等信息。
  2. 啓動 easy-mock,這種不是在後臺運行的方式,直接ctrl+c可關閉。
$ npm run dev
# 啓動後訪問 http://127.0.0.1:7300
複製代碼
  1. 使用pm2進行後臺啓動, 啓動成功後,在命令行中輸入 netstat -ntlp 查看正在使用的端口(如mongodb的27017,redis的6379,easy-mock的7300等)
$ [sudo] npm install pm2 -g
$ pm2 start app.js
複製代碼

easy-mock啓動後,經過瀏覽器能夠訪問, 輸入用戶名和密碼(若是用戶不存在會自動註冊)。

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提供了對StringNumberBooleanObjectArray等數據的模擬規則,只需按規則編寫,便可生成隨機數據,達到模擬數據效果。生成規則有 7 種格式:

  1. 'name|min-max': value
  2. 'name|count': value
  3. 'name|min-max.dmin-dmax': value
  4. 'name|min-max.dcount': value
  5. 'name|count.dmin-dmax': value
  6. 'name|count.dcount': value
  7. 'name|+step': value

使用了自定義的模擬數據後,須要注意如下幾點:

  • 使用了自定義的模擬數據規則後,前端在使用此接口進行模擬數據時,原來在swagger中定義的示例和返回值就會被自定義的數據覆蓋。
  • 當後端接口有變化,須要前端在easy-mock中從新同步新的swagger接口,同步前須要備份原來的自定義數據規則,不然,從新同步後,原來自定義的數據會swagger的接口定義覆蓋。所以,最好是先備份,而後針對有改動的地方進行規則修改便可。

4.接口文檔離線導出

通常在開發過程當中使用swagger文檔,直接使用瀏覽器訪問swagger-ui.html頁面便可知足要求,如有需求是須要導出離線接口文檔,整體能夠按如下思路進行:

  1. 使用swaggerv2/api-docs的url地址導出json文檔(不導出也能夠直接使用此url做爲輸入文檔的地址)
  2. 使用swagger2markup工具將json文檔轉爲asciidoc格式文檔。
  3. 使用asciidoctor工具將asciidoc文件轉html或pdf文件。

4.1 導出swagger的json文檔

在瀏覽器中訪問接口文檔頁面的地址是/swagger-ui.html,而swagger的規範文檔的地址是/v2/api-docsctrl+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>
複製代碼

此配置相對較長,其實主要分爲三部分:

  1. 轉換pdf使用的依賴,即<dependencies>元素,直接使用便可。
  2. 通用文檔配置<configuration>元素,其中須要注意的是<sourceDirectory>,此處須要配置上一個步驟生成的asiidoc文件目錄路徑。
  3. <executions>元素:因爲須要生成html和pdf兩種格式的文檔,所以分別使用executions來實現。其中html相對簡單,配置outputDirectorybackend元素指定輸出目錄路徑和文件格式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-stylesdirpdf-style,指定下載好的fonts目錄和themes目錄。

4.3.2 使用命令輸出文檔

使用命令mvn generate-resources,便可生成對應的html和pdf文檔到對應的目錄,分別是docs/html/all.htmldocs/html/all.pdf。效果以下圖所示:

  • html文檔

html

  • pdf文檔(缺失文字問題已解決)

pdf

5. 總結

本篇文章針對接口過濾顯示,前端mock數據和離線導出文檔等問題,提供相應的解決方法,包括接口過濾(包過濾、類註解過濾、方法註解過濾、分組過濾等方式),實現按需發佈指定接口的功能;使用easy-mock+swagger實現mock數據;使用maven插件實現接口文檔的離線導出。但願對有須要的同窗有所幫助。本文配套的示例工程地址https://github.com/mianshenglee/my-example/tree/master/springboot-swagger-demo/advance-swagger-demo,讀者可fork或pull下來,結合學習。

相關閱讀

springboot+swagger接口文檔企業實踐(上)

參考資料

關注個人公衆號,獲取更多技術記錄

mason技術記錄
相關文章
相關標籤/搜索