本文介紹一個很是好用的自動化生成 Restful API 文檔的工具——Api2Doc 它基於 SpringBoot ,原理相似於 Swagger2,但比 Swagger2 要簡單好用。html
此項目已經放到 github 中,須要源碼的朋友請點擊 這裏前端
新增瞭如下功能:java
@GetMapping
、 @PostMapping
、 @PutMapping
、 @DeleteMapping
、 @PatchMapping
; (以前只支持 @RequestMapping
。)@PathVariable
、 @RequestHeader
、 @CookieValue
、 @RequestPart
; (以前只支持 @RequestParam
。) 並在文檔頁面的「請求參數」表格,加上「參數形式」這一列。修復瞭如下 BUG:git
歡迎你們使用!github
在互聯網/移動互聯網軟件的研發過程當中,大多數研發團隊先後臺分工是很是明確的, 後臺工程師負責服務端系統的開發,通常是提供 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 還有好多細節沒作好,好比看這個圖:
紅框中的 API 其實對應的是同一個方法,之因此有這麼多,只是由於寫這個方法 時沒有指定 method:
@RestController @RequestMapping(value = "/user2") public class UserController2Swagger2 { @RequestMapping(value = "/do_something") public void doSomethingRequiredLogon() { } // 其它方法,這裏省略... }
(當沒指定 method 時,Spring Boot 會默認讓這個接口支持全部的 method)
所以,考慮到與其長長久久忍受 Swagger2 的各類不爽,不如花些時間作一個 更好用的「自動化文檔系統」,因而就誕生了本項目: 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 的核心設計理念,就是:它儘量經過智能分析,自動收集 生成文檔所需的信息,從而讓用戶少寫代碼。
說得有點抽象哈,下面咱們來正面回答這個問題,請你們注意這個類上有一個註解:
@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 好用的地方。
下面咱們就來全面講解它的用法,但願能夠幫助開發者們從文檔編寫的苦海中解脫出來。
若是是 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} 最新穩定版,請參考 這裏
本教程的示例代碼在 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); } }
而後咱們在 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 服務起做用了,就是這麼簡單!
Api2Doc 一共有 3 個註解:@Api2Doc、@ApiComment 及 @ApiError 。
@Api2Doc 用於對文檔的生成進行控制。
@Api2Doc 修飾在類上,表示這個類會參與到文檔生成過程當中,Api2Doc 服務 會掃描 Spring 容器中全部的 Controller 類,只有類上有 @Api2Doc 的類, 纔會被生成文檔,一個類對應於文檔頁面左側的一級菜單項,@Api2Doc 的 name 屬性則表示這個菜單項的名稱。
@Api2Doc 也能夠修飾在方法,不過在方法上的 @Api2Doc 一般是能夠省略, Api2Doc 服務會掃描這個類的全部帶有 @RequestMapping 的方法, 每一個這樣的方法對應文檔頁面的左側的二級菜單項, 菜單項的名稱取 @RequestMapping 的 name 屬性,固然您仍然能夠在方法上用 @Api2Doc 的 name 屬性進行重定義。
@ApiComment 用於對 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 用於定義錯誤碼,有的 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 中的 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 一樣能夠給一級菜單排序,規則是同樣的,這裏就不演示了。
有時候光有自動生成的 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 格式的文檔,以下圖所示:
文件名的格式爲 ${order}-${文檔名稱}.md,即 - 號前面的數字表示這個文檔的排序, 與 @Api2Doc 中的 order 屬性是同樣的,而 - 號後面是文檔名稱,也就是二級菜單的名稱。
所以,最後文檔的顯示效果爲:
看,手工編寫的補充文檔與自動生成的 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 都行。
配置後的顯示效果爲:
您在 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 服務。