Swagger使用————介面引數註解的使用缺陷
問題描述
在使用springboot開發web專案時,用到了swagger框架,來生成web api文件。但是其中有一項是舉例說明引數的結構,如下圖:
但是,這個功能真的是非常方便,因為可以讓前端開發人員第一時間得知引數的內部結構是什麼樣的,這尤其適用於那些json體結構的引數。網上的例子都是這樣的:
但是,我無論如何都弄不出來這個樣子,前前後後研究了有好幾個小時。
終於找出了問題。
問題原因
網上的api介面中,幾乎全是傳入一個完整的Java Bean物件,而不是傳JsonObject物件。
我的程式碼是這樣:
@ApiOperation(value = "新增景區下的特價門票", notes = "{\"scenicIdArr\" : [53361,53356]}") @PostMapping(value = "/special_price/add") public JSONObject addSpecialPrice(@RequestBody JSONObject scenicIdArr) { return sprSvc.addSpecialPrice(scenicIdArr); }
別人的程式碼是這樣:
@ApiOperation("更改使用者資訊")
@PostMapping("/updateUserInfo")
public int updateUserInfo(@RequestBody @ApiParam(name="使用者物件",value="傳入json格式",required=true) User user){
int num = userService.updateUserInfo(user);
return num;
}
經過比較,很容易就發現了問題,我的介面中的引數是無法得知內部資料結構的JSONObject型別,而別人的引數是一個已知其內部資料結構的User物件。
既然知道了原因,那我也將介面進行了一些修改:
建立一個符合我業務要求的資料結構實體類,然後將這個實體類作為引數傳入介面中:
@ApiOperation(value = "新增景區下的特價門票", notes = "{\"scenicIdArr\" : [53361,53356]}") @PostMapping(value = "/special_price/add") public JSONObject addSpecialPrice(@RequestBody ScenicIdArr scenicIdArr) { return null; } @ApiModel class ScenicIdArr { @ApiModelProperty(value = "景區id陣列") int[] scenicIdArr; }
上述程式碼中,我定義了一個成員內部類,並將實體類以@ApiModel進行註解。效果如下:
呵呵,沒有一丁點效果!我陷入了沉思... ...
於是我大膽的猜想:swagger框架可能是自動呼叫了get或set方法,並完成頁面渲染。
於是我又修改了程式碼:
@ApiOperation(value = "新增景區下的特價門票", notes = "{\"scenicIdArr\" : [53361,53356]}")
@PostMapping(value = "/special_price/add")
public JSONObject addSpecialPrice(@RequestBody ScenicIdArr scenicIdArr) {
return null;
}
@ApiModel
class ScenicIdArr {
@ApiModelProperty(value = "景區id陣列")
int[] scenicIdArr;
public int[] getScenicIdArr() {
return scenicIdArr;
}
}
我加了一個get方法,來獲取實體類中的屬性,看一下效果:
上圖中可以看到,不論是整體的實體類結構,還是欄位上的註釋“景區id陣列”都很好的顯示了出來。這樣,我們在頁面點選小黃框的時候,就可以將資料自動的載入到引數填寫的白框內。
這樣,我們省去了手動書寫結構和key值的過程,而只需要我們輸入具體的value值即可。
由於本篇部落格並不是教你如何使用spring-boot-starter-swagger自動依賴配置模組中的各種註解如何使用,因此,此處只是簡單解析了一下介面引數的模板生成方式。
但是,題目中既然提到了這個功能的缺陷,就不得不回過頭來吐槽一下這個破JB玩意兒!
吐槽一下
這個在小黃框顯示對外介面引數結構的功能真的應該說是非常實用的一個功能,我並不知道這個功能具體的名稱是什麼,暫且就稱它為“引數樣例功能”。
為什麼說這個功能非常實用?
首先,書寫簡便。REST介面風格的引數多以json結構體傳輸資料,而這樣一個自動生成結構體的功能,可以為我們免去書寫大量括號、冒號、逗號、引號、空格等json體的必須元素,而且自動排版,避免手寫出錯,達到零錯誤。在真正通過頁面的api介面測試的時候,只需要簡單輸入幾個value值就可以“try it out”了,著實提高了不少效率。
其次,方便前端開發。我們都知道,不論是傳入JavaBean物件,還是傳入沒有在後端強制型別約束的Json字串,前端呼叫controller中的介面時,僅僅是傳入一個key-value的結構,才不會管你什麼JavaBean。對於springboot,其一系列內嵌的HttpMessageConverter會將json結構轉化成對應的JavaBean,再交給Controller。前端開發人員甚至可以將小黃框內的內容,直接拷貝過來,稍作修改(value賦值)即可完成對後端介面對接的全部編碼。
第三,覆蓋了其他部分註解。個人認為,swagger中的註解關於引數註釋方面,有些重複,這一點不做展開討論,且僅僅是個人觀點,可以參考官方api文件體會一下。另外關於response一類的註解,完全沒有必要。返回值是什麼結構的,完全可以通過“try it out”呼叫一次介面即可瞭解到。估計swagger開發團隊考慮到功能的完整性,或者在後端由於某種原因導致介面不可用而做的一種補足方案(比如,資料庫中暫無資料等原因)。
雖然這個功能實用,但是依然存在一個很彆扭的情況。
那就是,我們不得不因為要在“引數樣例功能”中展示我們的引數結構而建立一個Java Bean。不論這個Java Bean在後臺邏輯中有無實際意義。
就比方說我之前的那個介面:
@ApiOperation(value = "新增景區下的特價門票", notes = "{\"scenicIdArr\" : [53361,53356]}")
@PostMapping(value = "/special_price/add")
public JSONObject addSpecialPrice(@RequestBody JSONObject scenicIdArr)
我需要拿到一個有景區id組成的json體的資料結構,然後跑到service中去處理,可能我的這種api結構並不規範,但是不可能不存在這樣一種情況:僅僅規定一個json結構,而不是Java Bean來作為引數進行處理。(抱歉,最近在看《Thinking In Java》句子寫起來有那麼一點模仿布魯斯埃克爾的腔調)
我查找了很多資料,包括官方的API說明,並沒有很好解決方案。於是,才有了後面的成員內部類的出現。但是這種程式碼除了方便測試以外完全沒有實際意義。如果為每個以json結構體作為引數的介面另起一個class檔案去明確傳入引數的結構,就顯得有些愚蠢。它會使你的程式碼非常的臃腫和凌亂。
這就是我說的swagger的這個缺陷,以純json結構體作為引數的介面,無法實現非常重要的“引數樣例功能”。
綜上,就是我對swagger框架的一點看法和總結,如有疑問,歡迎文末留言。