之前通過Spring Boot 2.x基礎教程:使用Swagger2構建強大的API文檔一文,我們學習了如何使用Swagger為Spring Boot項目自動生成API文檔,有不少用戶留言問了關於文檔內容的組織以及排序問題。所以,就特別開一篇詳細說說Swagger中文檔內容如何來組織以及其中各個元素如何控制前后順序的具體配置方法。
接口的分組
我們在Spring Boot中定義各個接口是以Controller
作為第一級維度來進行組織的,Controller
與具體接口之間的關系是一對多的關系。我們可以將同屬一個模塊的接口定義在一個Controller
里。默認情況下,Swagger是以Controller
為單位,對接口進行分組管理的。這個分組的元素在Swagger中稱為Tag
,但是這里的Tag
與接口的關系並不是一對多的,它支持更豐富的多對多關系。
默認分組
首先,我們通過一個簡單的例子,來看一下默認情況,Swagger是如何根據Controller來組織Tag與接口關系的。定義兩個Controller
,分別負責教師管理與學生管理接口,比如下面這樣:
@RestController
@RequestMapping(value = "/teacher")
static class TeacherController {
@GetMapping("/xxx")
public String xxx() {
return "xxx";
}
}
@RestController
@RequestMapping(value = "/student")
static class StudentController {
@ApiOperation("獲取學生清單")
@GetMapping("/list")
public String bbb() {
return "bbb";
}
@ApiOperation("獲取教某個學生的老師清單")
@GetMapping("/his-teachers")
public String ccc() {
return "ccc";
}
@ApiOperation("創建一個學生")
@PostMapping("/aaa")
public String aaa() {
return "aaa";
}
}
啟動應用之后,我們可以看到Swagger中這兩個Controller是這樣組織的:
圖中標出了Swagger默認生成的Tag
與Spring Boot中Controller
展示的內容與位置。
自定義默認分組的名稱
接着,我們可以再試一下,通過@Api
注解來自定義Tag
,比如這樣:
@Api(tags = "教師管理")
@RestController
@RequestMapping(value = "/teacher")
static class TeacherController {
// ...
}
@Api(tags = "學生管理")
@RestController
@RequestMapping(value = "/student")
static class StudentController {
// ...
}
再次啟動應用之后,我們就看到了如下的分組內容,代碼中@Api
定義的tags
內容替代了默認產生的teacher-controller
和student-controller
。
合並Controller分組
到這里,我們還都只是使用了Tag
與Controller
一一對應的情況,Swagger中還支持更靈活的分組!從@Api
注解的屬性中,相信聰明的讀者一定已經發現tags
屬性其實是個數組類型:
我們可以通過定義同名的Tag
來匯總Controller
中的接口,比如我們可以定義一個Tag
為“教學管理”,讓這個分組同時包含教師管理和學生管理的所有接口,可以這樣來實現:
@Api(tags = {"教師管理", "教學管理"})
@RestController
@RequestMapping(value = "/teacher")
static class TeacherController {
// ...
}
@Api(tags = {"學生管理", "教學管理"})
@RestController
@RequestMapping(value = "/student")
static class StudentController {
// ...
}
最終效果如下:
更細粒度的接口分組
通過@Api
可以實現將Controller
中的接口合並到一個Tag
中,但是如果我們希望精確到某個接口的合並呢?比如這樣的需求:“教學管理”包含“教師管理”中所有接口以及“學生管理”管理中的“獲取學生清單”接口(不是全部接口)。
那么上面的實現方式就無法滿足了。這時候發,我們可以通過使用@ApiOperation
注解中的tags
屬性做更細粒度的接口分類定義,比如上面的需求就可以這樣子寫:
@Api(tags = {"教師管理","教學管理"})
@RestController
@RequestMapping(value = "/teacher")
static class TeacherController {
@ApiOperation(value = "xxx")
@GetMapping("/xxx")
public String xxx() {
return "xxx";
}
}
@Api(tags = {"學生管理"})
@RestController
@RequestMapping(value = "/student")
static class StudentController {
@ApiOperation(value = "獲取學生清單", tags = "教學管理")
@GetMapping("/list")
public String bbb() {
return "bbb";
}
@ApiOperation("獲取教某個學生的老師清單")
@GetMapping("/his-teachers")
public String ccc() {
return "ccc";
}
@ApiOperation("創建一個學生")
@PostMapping("/aaa")
public String aaa() {
return "aaa";
}
}
效果如下圖所示:
內容的順序
在完成了接口分組之后,對於接口內容的展現順序又是眾多用戶特別關注的點,其中主要涉及三個方面:分組的排序、接口的排序以及參數的排序,下面我們就來逐個說說如何配置與使用。
分組的排序
關於分組排序,也就是Tag的排序。目前版本的Swagger支持並不太好,通過文檔我們可以找到關於Tag排序的配置方法。
第一種:原生Swagger用戶,可以通過如下方式:
第二種:Swagger Starter用戶,可以通過修改配置的方式:
swagger.ui-config.tags-sorter=alpha
似乎找到了希望,但是其實這塊並沒有什么可選項,一看源碼便知:
public enum TagsSorter {
ALPHA("alpha");
private final String value;
TagsSorter(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
public static TagsSorter of(String name) {
for (TagsSorter tagsSorter : TagsSorter.values()) {
if (tagsSorter.value.equals(name)) {
return tagsSorter;
}
}
return null;
}
}
是的,Swagger只提供了一個選項,就是按字母順序排列。那么我們要如何實現排序呢?這里筆者給一個不需要擴展源碼,僅依靠使用方式的定義來實現排序的建議:為Tag的命名做編號。比如:
@Api(tags = {"1-教師管理","3-教學管理"})
@RestController
@RequestMapping(value = "/teacher")
static class TeacherController {
// ...
}
@Api(tags = {"2-學生管理"})
@RestController
@RequestMapping(value = "/student")
static class StudentController {
@ApiOperation(value = "獲取學生清單", tags = "3-教學管理")
@GetMapping("/list")
public String bbb() {
return "bbb";
}
// ...
}
由於原本存在按字母排序的機制在,通過命名中增加數字來幫助排序,可以簡單而粗暴的解決分組問題,最后效果如下:
接口的排序
在完成了分組排序問題(雖然不太優雅...)之后,在來看看同一分組內各個接口該如何實現排序。同樣的,凡事先查文檔,可以看到Swagger也提供了相應的配置,下面也分兩種配置方式介紹:
第一種:原生Swagger用戶,可以通過如下方式:
第二種:Swagger Starter用戶,可以通過修改配置的方式:
swagger.ui-config.operations-sorter=alpha
很慶幸,這個配置不像Tag的排序配置沒有可選項。它提供了兩個配置項:alpha
和method
,分別代表了按字母表排序以及按方法定義順序排序。當我們不配置的時候,改配置默認為alpha
。兩種配置的效果對比如下圖所示:
參數的排序
完成了接口的排序之后,更細粒度的就是請求參數的排序了。默認情況下,Swagger對Model參數內容的展現也是按字母順序排列的。所以之前教程中的User對象在文章中展現如下:
如果我們希望可以按照Model中定義的成員變量順序來展現,那么需要我們通過@ApiModelProperty
注解的position
參數來實現位置的設置,比如:
@Data
@ApiModel(description = "用戶實體")
public class User {
@ApiModelProperty(value = "用戶編號", position = 1)
private Long id;
@NotNull
@Size(min = 2, max = 5)
@ApiModelProperty(value = "用戶姓名", position = 2)
private String name;
@NotNull
@Max(100)
@Min(10)
@ApiModelProperty(value = "用戶年齡", position = 3)
private Integer age;
@NotNull
@Email
@ApiModelProperty(value = "用戶郵箱", position = 4)
private String email;
}
最終效果如下:
小結
本文詳細的介紹了Swagger中對接口內容的組織控制。有些問題並沒有通過配置來解決,也可能是對文檔或源碼內容的了解不夠深入。如果讀者有更好的實現方案,歡迎提出與交流。
代碼示例
本文的完整工程可以查看下面倉庫中的chapter2-4
目錄:
- Github:https://github.com/dyc87112/SpringBoot-Learning/tree/2.x
- Gitee:https://gitee.com/didispace/SpringBoot-Learning/tree/2.x
如果您覺得本文不錯,歡迎Star支持,您的關注是我堅持的動力!
相關資料
- https://swagger.io/docs/
- http://blog.didispace.com/spring-boot-learning-21-2-2/
- https://github.com/SpringForAll/spring-boot-starter-swagger
歡迎關注我的公眾號:程序猿DD,獲得獨家整理的學習資源和日常干貨推送。
如果您對我的專題內容感興趣,也可以關注我的博客:didispace.com