還在用 Swagger2 生成 Restful API 文檔?來試試 Api2Doc 吧!

本文介紹一個很是好用的自動化生成 Restful API 文檔的工具——Api2Doc 它基於 SpringBoot ,原理相似於 Swagger2,但比 Swagger2 要簡單好用。html

此項目已經放到 github 中,須要源碼的朋友請點擊 這裏前端

最新版 1.0.2 已發佈(於 2018-04-06 )

新增瞭如下功能:java

  1. 支持各類用 @XxxMapping 修飾的方法生成文檔,包括: @GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping; (以前只支持 @RequestMapping 。)
  2. 支持各類形式的參數,包括: @PathVariable@RequestHeader@CookieValue@RequestPart; (以前只支持 @RequestParam 。) 並在文檔頁面的「請求參數」表格,加上「參數形式」這一列。
  3. 文檔頁面,「URL示例」改成「請求示例」, 請求示例爲 curl 命令格式,並支持全部的 HTTP 方法 , (以前是 URL 格式,而且只支持了 GET 方法)。

修復瞭如下 BUG:git

  1. 修復當返回類型爲簡單類型時未能顯示在文檔的 BUG。

歡迎你們使用!github

目錄

  • 項目背景
  • Api2Doc 簡介
  • 引入 Api2Doc 依賴
  • 啓用 Api2Doc 服務
  • 給 Controller 類上添加文檔註解
  • @Api2Doc 註解詳述
  • @ApiComment 註解詳述
  • @ApiError 註解詳述
  • 給文檔菜單項排序
  • 補充自定義文檔
  • 定製文檔的歡迎頁
  • 定製文檔的標題及圖標
  • 關閉 Api2Doc 服務

項目背景

在互聯網/移動互聯網軟件的研發過程當中,大多數研發團隊先後臺分工是很是明確的, 後臺工程師負責服務端系統的開發,通常是提供 HTTP/HTTPS 的 Restful API 接口, 前端工程師則負責 Android、iOS、H5頁面的開發,須要調用 Restful API 接口。web

這就須要有一套 Restful API 文檔,以幫助兩方在 API 接口進行溝通,並達成一致意見。 通常狀況下,編寫文檔的工做都會落在後臺工程師身上,畢竟 API 是他們提供的嘛。spring

但問題是,編寫 Restful API 文檔是一件既繁瑣、又費時、還對提升技術能力沒啥幫助的苦差事, 尤爲在是快速迭代、需求頻繁修改的項目中,改了代碼還要同步改文檔, 哪點改錯了或改漏了均可能產生先後端實現的不一致,致使聯調時發現 BUG, 這個鍋最終仍是要後臺工程師來背(寶寶內心苦啊...)。後端

所以,業界就出現了一些根據代碼自動生成 Restful API 文檔的開源項目, 與 Spring Boot 結合比較好的是 Swagger2,Swagger2 經過讀取 Controller 代碼中的註解信息,來自動生成 API 文檔,能夠節省大量的手工編寫文檔的工做量。api

本項目做者以前也是用的 Swagger2,但發現 Swagger2 也有好多地方用得不爽:網絡

第一,Swagger2 的註解很是臃腫,咱們看下這段代碼:

@RestController
@RequestMapping(value = "/user3")
public class UserController2Swagger2 {

    @ApiOperation(value = "獲取指定id用戶詳細信息",
            notes = "根據user的id來獲取用戶詳細信息",
            httpMethod = "GET")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "userName", value = "用戶名",
                    paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "password", value = "用戶密碼",
                    paramType = "query", required = true, dataType = "String")
    })
    @RequestMapping(name = "用戶註冊", value = "/regist",
            method = RequestMethod.GET)
    public UserInfo regist(@RequestParam("userName") String userName,
                           @RequestParam("password") String password) {
        return new UserInfo();
    }
}

@ApiOperation、@ApiImplicitParam 都是 Swagger2 提供的註解,用於定義 API 信息。 其實,API 方法自己就包含了不少信息,如HTTP Method、參數名、參數類型等等, 像 @ApiImplicitParam 中除了 value 屬性有用外,其它都是重複描述。

第二,Swagger2 的頁面排版不太友好,它是一個垂直排列的方式,不利於信息的展現。 而且看 API 詳細信息還要一個個展開,中間還夾雜着測試的功能,反正做爲文檔是不易於閱讀; 至於做爲測試工具嘛...,如今專業的測試工具也有不少,測試人員好像也不選它。

第三,Swagger2 還有好多細節沒作好,好比看這個圖:

swgger2-1.png

紅框中的 API 其實對應的是同一個方法,之因此有這麼多,只是由於寫這個方法 時沒有指定 method:

@RestController
@RequestMapping(value = "/user2")
public class UserController2Swagger2 {
    
    @RequestMapping(value = "/do_something")
    public void doSomethingRequiredLogon() {
    }
    
    // 其它方法,這裏省略...
}

(當沒指定 method 時,Spring Boot 會默認讓這個接口支持全部的 method)

所以,考慮到與其長長久久忍受 Swagger2 的各類不爽,不如花些時間作一個 更好用的「自動化文檔系統」,因而就誕生了本項目: Api2Doc 。

Api2Doc 簡介

Api2Doc 專一於 Restful API 文檔的自動生成,它的原理與 Swagger2 是相似的, 都是經過反射,分析 Controller 中的信息生成文檔,但它要比 Swagger2 好不少。

最大的不一樣是: Api2Doc 比 Swagger2 要少寫不少代碼

舉個例子,使用 Swagger2 的代碼是這樣的:

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @ApiOperation(value = "添加用戶", httpMethod = "POST",
            notes = "向用戶組中添加用戶,能夠指定用戶的類型")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "group", value = "用戶組名",
                    paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "name", value = "用戶名",
                    paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "type", value = "用戶類型",
                                paramType = "query", required = true, dataType = "String")
    })
    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public User addUser(String group, String name, String type) {
        return null; // TODO:  還未實現。
    }
}

咱們看下使用 Api2Doc 註解修飾後的代碼:

@Api2Doc(id = "users")
@ApiComment(seeClass = User.class)
@RestController
@RequestMapping(value = "/api2doc/demo2")
public class UserController2 {

    @ApiComment("向用戶組中添加用戶,能夠指定用戶的類型")
    @RequestMapping(name = "添加用戶",
            value = "/user", method = RequestMethod.POST)
    public User addUser(String group, String name, String type) {
        return null; // TODO:  還未實現。
    }
    
    // 其它方法,這裏省略...
}

看,Api2Doc 僅須要在方法上加上 @Api2Doc @ApiComment 註解等極少數代碼, 但它生成的文檔可一點不含糊,以下圖所示:

api2doc-2-1.png api2doc-2-2.png

有的朋友可能會以爲很奇怪:文檔頁面上的說明、示例值等內容,在代碼中沒有寫啊, 這些是哪來的呢?

這裏涉及到 Api2Doc 的核心設計理念,就是:它儘量經過智能分析,自動收集 生成文檔所需的信息,從而讓用戶少寫代碼

說得有點抽象哈,下面咱們來正面回答這個問題,請你們注意這個類上有一個註解:

@ApiComment(seeClass = User.class)

它意思是: 在 API 方法上遇到沒寫說明信息時,請參照 User 類中的定義的說明信息。

下面是 User 類的代碼:

public class User {

    @ApiComment(value = "用戶id", sample = "123")
    private Long id;

    @ApiComment(value = "用戶名", sample = "terran4j")
    private String name;

    @ApiComment(value = "帳號密碼", sample = "sdfi23skvs")
    private String password;

    @ApiComment(value = "用戶所在的組", sample = "研發組")
    private String group;

    @ApiComment(value = "用戶類型", sample = "admin")
    private UserType type;

    @ApiComment(value = "是否已刪除", sample = "true")
    @RestPackIgnore
    private Boolean deleted;

    @ApiComment(value = "建立時間\n也是註冊時間。")
    private Date createTime;

    // 省略  getter / setter 方法。
}

你們看明白了沒? API 方法中的參數,若是與 User 類的屬性同名的話,就用類 屬性的 @ApiComment 說明信息自動填充。

其實這也符合實際的業務邏輯。由於在大部分項目中,有的字段會在多個實體類、 多個 API 方法中用到,徹底沒有必要重複編寫其說明信息,只要有一個地方定義好了, 而後其它地方參照就好了。

固然,這只是 Api2Doc 比 Swagger2 好用的特性之一,還有很多比 Swagger2 好用的地方。

下面咱們就來全面講解它的用法,但願能夠幫助開發者們從文檔編寫的苦海中解脫出來。

引入 Api2Doc 依賴

若是是 maven ,請在 pom.xml 中添加依賴,以下所示:

<dependency>
            <groupId>com.github.terran4j</groupId>
            <artifactId>terran4j-commons-api2doc</artifactId>
            <version>${api2doc.version}</version>
        </dependency>

若是是 gradle,請在 build.gradle 中添加依賴,以下所示:

compile "com.github.terran4j:terran4j-commons-api2doc:${api2doc.version}"

${api2doc.version} 最新穩定版,請參考 這裏

啓用 Api2Doc 服務

本教程的示例代碼在 src/test/java 目錄的 com.terran4j.demo.api2doc 中, 您也能夠從 這裏 獲取到。

首先,咱們須要在有 @SpringBootApplication 註解的類上,添加 @EnableApi2Doc 註解,以啓用 Api2Doc 服務,以下代碼所示:

package com.terran4j.demo.api2doc;

import com.terran4j.commons.api2doc.config.EnableApi2Doc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//  文檔訪問地址: http://localhost:8080/api2doc/home.html
@EnableApi2Doc
@SpringBootApplication
public class Api2DocDemoApp {

    public static void main(String[] args) {
        SpringApplication.run(Api2DocDemoApp.class, args);
    }

}

給 Controller 類上添加文檔註解

而後咱們在 RestController 類添加 @Api2Doc 註解,在須要有文檔說明的地方 添加 @ApiComment 註解便可,以下所示:

package com.terran4j.demo.api2doc;

import com.terran4j.commons.api2doc.annotations.Api2Doc;
import com.terran4j.commons.api2doc.annotations.ApiComment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@Api2Doc(id = "demo1", name = "用戶接口1")
@ApiComment(seeClass = User.class)
@RestController
@RequestMapping(value = "/api2doc/demo1")
public class UserController1 {

    @ApiComment("添加一個新的用戶。")
    @RequestMapping(name = "新增用戶",
            value = "/user", method = RequestMethod.POST)
    public User addUser(String group, String name,
                        @ApiComment("用戶類型") UserType type) {
        return null; // TODO:  還未實現。
    }
}

這個方法的返回類型 User 類的定義爲:

public class User {

    @ApiComment(value = "用戶id", sample = "123")
    private Long id;

    @ApiComment(value = "用戶名", sample = "terran4j")
    private String name;

    @ApiComment(value = "帳號密碼", sample = "sdfi23skvs")
    private String password;

    @ApiComment(value = "用戶所在的組", sample = "研發組")
    private String group;

    @ApiComment(value = "用戶類型", sample = "admin")
    private UserType type;

    @ApiComment(value = "是否已刪除", sample = "true")
    @RestPackIgnore
    private Boolean deleted;

    @ApiComment(value = "建立時間\n也是註冊時間。")
    private Date createTime;

    // 省略  getter / setter 方法。
}

以及 type 屬性的類型,也就是 UserType 類的定義爲:

package com.terran4j.demo.api2doc;

import com.terran4j.commons.api2doc.annotations.ApiComment;

public enum UserType {

    @ApiComment("管理員")
    admin,

    @ApiComment("普通用戶")
    user
}

編寫好代碼後,咱們運行 main 函數,訪問 Api2Doc 的主頁面:

http://localhost:8080/api2doc/home.html

文檔頁面以下:

api2doc-3-1.png api2doc-3-2.png

說明 Api2Doc 服務起做用了,就是這麼簡單!

@Api2Doc 註解詳述

Api2Doc 一共有 3 個註解:@Api2Doc、@ApiComment 及 @ApiError 。

@Api2Doc 用於對文檔的生成進行控制。

@Api2Doc 修飾在類上,表示這個類會參與到文檔生成過程當中,Api2Doc 服務 會掃描 Spring 容器中全部的 Controller 類,只有類上有 @Api2Doc 的類, 纔會被生成文檔,一個類對應於文檔頁面左側的一級菜單項,@Api2Doc 的 name 屬性則表示這個菜單項的名稱。

@Api2Doc 也能夠修飾在方法,不過在方法上的 @Api2Doc 一般是能夠省略, Api2Doc 服務會掃描這個類的全部帶有 @RequestMapping 的方法, 每一個這樣的方法對應文檔頁面的左側的二級菜單項, 菜單項的名稱取 @RequestMapping 的 name 屬性,固然您仍然能夠在方法上用 @Api2Doc 的 name 屬性進行重定義。

@ApiComment 註解詳述

@ApiComment 用於對 API 進行說明,它能夠修飾在不少地方:

  • 修飾在類上,表示對這組 API 接口進行說明;
  • 修飾在方法上,表示對這個 API 接口進行說明;
  • 修飾在參數上,表示對這個 API 接口的請求參數進行說明;
  • 修飾在返回類型的屬性上,表示對這個 API 接口的返回字段進行說明;
  • 修飾在枚舉項上,表示對枚舉項進行說明;

若是相同名稱、相贊成義的屬性或參數字段,其說明已經在別的地方定義過了, 能夠用 @ApiComment 的 seeClass 屬性表示採用指定類的同名字段上的說明信息, 因此如這段代碼:

@Api2Doc(id = "demo1", name = "用戶接口1")
@ApiComment(seeClass = User.class)
@RestController
@RequestMapping(value = "/api2doc/demo1")
public class UserController1 {

    @ApiComment("添加一個新的用戶。")
    @RequestMapping(name = "新增用戶",
            value = "/user", method = RequestMethod.POST)
    public User addUser(String group, String name, UserType type) {
        return null; // TODO:  還未實現。
    }
}

雖然 group, name ,type 三個參數沒有用 @ApiComment 進行說明, 但因爲這個類上有 @ApiComment(seeClass = User.class) , 所以只要 User 類中有 group, name ,type 字段而且有 @ApiComment 的說明就好了。

@ApiError 註解詳述

@ApiError 用於定義錯誤碼,有的 API 方法在執行業務邏輯時會產生錯誤, 出錯後會在返回報文包含錯誤碼,以方便客戶端根據錯誤碼做進一步的處理, 所以也須要在 API 文檔上體現錯誤碼的說明。

以下代碼演示了 @ApiError 的用法:

@Api2Doc(id = "demo", name = "用戶接口", order = 0)
@ApiComment(seeClass = User.class)
@RestController
@RequestMapping(value = "/src/test/resources/demo")
public class UserController {
    
    @Api2Doc(order = 50)
    @ApiComment("根據用戶id,刪除指定的用戶")
    @ApiError(value = "user.not.found", comment = "此用戶不存在!")
    @ApiError(value = "admin.cant.delete", comment = "不容許刪除管理員用戶!")
    @RequestMapping(name = "刪除指定用戶",
            value = "/user/{id}", method = RequestMethod.DELETE)
    public void delete(@PathVariable("id") Long id) {
    }
}

@ApiError 的 value 屬性表示錯誤碼,comment 表示錯誤碼的說明。

錯誤碼信息會顯示在文檔的最後面,效果以下所示:

api2doc-7.png

給文檔菜單項排序

咱們能夠用 @Api2Doc 中的 order 屬性給菜單項排序,order 的值越小, 該菜單項就越排在前面,好比對於這段代碼:

package com.terran4j.demo.api2doc;

import com.terran4j.commons.api2doc.annotations.Api2Doc;
import com.terran4j.commons.api2doc.annotations.ApiComment;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Api2Doc(id = "demo2", name = "用戶接口2", order = 1)
@ApiComment(seeClass = User.class)
@RestController
@RequestMapping(value = "/api2doc/demo2")
public class UserController2 {

    @Api2Doc(order = 10)
    @ApiComment("添加一個新的用戶。")
    @RequestMapping(name = "新增用戶",
            value = "/user", method = RequestMethod.POST)
    public User addUser(
            @ApiComment("用戶組名稱") String group,
            @ApiComment("用戶名稱") String name,
            @ApiComment("用戶類型") UserType type) {
        return null; // TODO:  還未實現。
    }

    @Api2Doc(order = 20)
    @ApiComment("根據用戶id,查詢此用戶的信息")
    @RequestMapping(name = "查詢單個用戶",
            value = "/user/{id}", method = RequestMethod.GET)
    public User getUser(@PathVariable("id") Long id) {
        return null; // TODO:  還未實現。
    }

    @Api2Doc(order = 30)
    @ApiComment("查詢全部用戶,按註冊時間進行排序。")
    @RequestMapping(name = "查詢用戶列表",
            value = "/users", method = RequestMethod.GET)
    public List<User> getUsers() {
        return null; // TODO:  還未實現。
    }

    @Api2Doc(order = 40)
    @ApiComment("根據指定的組名稱,查詢該組中的全部用戶信息。")
    @RequestMapping(name = "查詢用戶組",
            value = "/group/{group}", method = RequestMethod.GET)
    public UserGroup getGroup(@PathVariable("group") String group) {
        return null; // TODO:  還未實現。
    }
}

顯示的結果爲:

api2doc-3.png

在類上的 @Api2Doc 一樣能夠給一級菜單排序,規則是同樣的,這裏就不演示了。

補充自定義文檔

有時候光有自動生成的 API 文檔彷佛還不太完美,或許咱們想補充點別的什麼東西, 好比: 對項目的背景介紹、技術架構說明之類,那這個要怎麼弄呢?

Api2Doc 容許用 md 語法手工編寫文檔,並集成到自動生成的 API 文檔之中,方法以下:

首先,要在類上的 @Api2Doc 定義 id 屬性,好比對下面這個類:

package com.terran4j.demo.api2doc;

import com.terran4j.commons.api2doc.annotations.Api2Doc;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api2Doc(id = "demo3", name = "用戶接口3")
@RestController
@RequestMapping(value = "/api2doc/demo3")
public class UserController3 {

    @Api2Doc(order = 10)
    @RequestMapping(name = "接口1", value = "/m1")
    public void m1() {
    }

    @Api2Doc(order = 20)
    @RequestMapping(name = "接口2", value = "/m2")
    public void m2() {
    }
}

@Api2Doc(id = "demo3", name = "用戶接口3") 表示:對應的一級菜單「用戶接口3」 的 id 爲 demo3。

而後,咱們在 src/main/resources 中建立目錄 api2doc/demo3, 前面的 api2doc 是固定的,後面的 demo3 表示這個目錄中的文檔是添加到 id 爲 demo3 的一級文檔菜單下。

而後咱們在 api2doc/demo3 目錄中編寫 md 格式的文檔,以下圖所示:

api2doc-4.png

文件名的格式爲 ${order}-${文檔名稱}.md,即 - 號前面的數字表示這個文檔的排序, 與 @Api2Doc 中的 order 屬性是同樣的,而 - 號後面是文檔名稱,也就是二級菜單的名稱。

所以,最後文檔的顯示效果爲:

api2doc-5.png

看,手工編寫的補充文檔與自動生成的 API 文檔,經過 order 進行排序組合在一塊兒, 看起來毫無違和感。

定製文檔的歡迎頁

每次訪問文檔頁面 http://localhost:8080/api2doc/home.html 時, 中間的內容是很是簡單的一句:

歡迎使用 Api2Doc !

這彷佛有點不太好,不過不要緊,咱們能夠編寫本身的歡迎頁。

方法很簡單,在 src/main/resources 目錄的 api2doc 目錄下,建立一個名爲 welcome.md 的文件(這個名稱是固定的),而後用 md 語法編寫內容就能夠。

配置文檔的標題及圖標

能夠在 application.yml 中配置文檔的標題及圖標,以下所示:

api2doc:
  title: Api2Doc示例項目——接口文檔
  icon: https://spring.io/img/homepage/icon-spring-framework.svg

圖標爲一個全路徑 URL,或本站點相對路徑 URL 都行。

配置後的顯示效果爲:

api2doc-6.png

關閉 Api2Doc 服務

您在 application.yml 中配置 api2doc.enabled 屬性,以開啓或關閉 Api2Doc 服務,如:

# 本地環境
api2doc:
  title: Api2Doc示例項目——接口文檔
  icon: https://spring.io/img/homepage/icon-spring-framework.svg

---
# 線上環境
spring:
  profiles: online

api2doc:
  enabled: false

api2doc.enabled 爲 false 表示關閉 Api2Doc 服務,不寫或爲 true 表示啓用。

因爲 Api2Doc 服務沒有訪問權限校驗,建議您在受信任的網絡環境(如公司內網) 中才啓用 Api2Doc 服務。

相關文章
相關標籤/搜索