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

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

1.引言

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

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

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

  1. 接口過濾:通過包過濾、類註解過濾、方法註解過濾、分組過濾等方式,實現按需發佈指定接口的功能。
  2. 使用easy-mock+swagger實現mock數據。
  3. 使用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()函數進行過濾,把需要顯示的接口使用函數進行返回,注意,此處的參數enableClassFilterenableMethodFiltergroupsFilters分別對應後面的過濾方法,定義如下:

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,具有以下特徵:

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技術記錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章