SpringBoot系列教程之定義介面返回型別的幾種方式
SpringBoot 系列教程之定義介面返回型別的幾種方式
實現一個 web 介面返回 json 資料,基本上是每一個 javaer 非常熟悉的事情了;那麼問題來了,如果我有一個介面,除了希望返回 json 格式的資料之外,若也希望可以返回 xml 格式資料可行麼?
答案當然是可行的,接下來我們將介紹一下,一個介面的返回資料型別,可以怎麼處理
I. 專案搭建
本文建立的例項工程採用 SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ idea
進行開發
1. pom 依賴
具體的 SpringBoot 專案工程建立就不贅述了,對於 pom 檔案中,需要重點關注下面兩個依賴類
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> </dependencies>
注意 jackson-datafromat-xml
這個依賴,加上這個主要時為了支援返回 xml 格式的資料
II. 返回型別設定的多種方式
正常來講,一個 RestController 的介面,預設返回的是 Json 格式資料,當我們引入了上面的 xml 包之後,會怎樣呢?返回的還是 json 麼?
1.通過 produce 設定返回型別
如果一個介面希望返回 json 或者 xml 格式的資料,最容易想到的方式就是直接設定 RequestMapping
註解中的 produce 屬性
這個值主要就是用來設定這個介面響應頭中的 content-type
; 如我們現在有兩個介面,一個指定返回 json 格式資料,一個指定返回 xml 格式資料,可以如下寫
@RestController public class IndexRest{ @Data public static class ResVo<T> { private int code; private String msg; private T data; public ResVo(int{ this.code = code; this.msg = msg; this.data = data; } } @GetMapping(path = "/xml", produces = {MediaType.APPLICATION_XML_VALUE}) public ResVo<String> xml(){ return new ResVo<>(0, "ok", "返回xml"); } @GetMapping(path = "/json", produces = {MediaType.APPLICATION_JSON_VALUE}) public ResVo<String> json(){ return new ResVo<>(0, "ok", "返回json"); } }
上面的實現中
produces = application/xml produces = applicatin/json
接下來我們訪問一下看看返回的是否和預期一致
從上面截圖也可以看出,xml 介面返回的是 xml 格式資料;json 介面返回的是 json 格式資料
2. 通過請求頭 accept 設定返回型別
上面的方式,非常直觀,自然我們就會有一個疑問,當介面上不指定 produces 屬性時,直接訪問會怎麼表現呢?
@GetMapping(path = "/") public ResVo<String> index(){ return new ResVo<>(0, "ok", "簡單的測試"); }
請注意上面的截圖,兩種訪問方式返回的資料型別不一致
application/xhtml+xml
那麼問題來了,為什麼兩者的表現形式不一致呢?
對著上面的圖再看三秒,會發現主要的差別點就在於請求頭 Accept
不同;我們可以通過這個請求頭引數,來要求服務端返回我希望的資料型別
如指定返回 json 格式資料
curl 'http://127.0.0.1:8080' -H 'Accept:application/xml' -iv curl 'http://127.0.0.1:8080' -H 'Accept:application/json'
從上面的執行結果也可以看出,返回的型別與預期的一致;
說明
請求頭可以設定多種 MediaType,用英文逗號分割,後端介面會根據自己定義的 produce 與請求頭希望的 mediaType 取交集,至於最終選擇的順序則以 accept 中出現的順序為準
看一下實際的表現來驗證下上面的說法
通過請求頭來控制返回資料型別的方式可以說是非常經典的策略了,(遵循 html 協議還有什麼好說的呢!)
3. 請求引數來控制返回型別
除了上面介紹的兩種方式之外,還可以考慮為所有的介面,增加一個根據特定的請求引數來控制返回的型別的方式
比如我們現在定義,所有的介面可以選傳一個引數 mediaType
,如果值為 xml,則返回 xml 格式資料;如果值為 json,則返回 json 格式資料
當不傳時,預設返回 json 格式資料
基於此,我們主要藉助 mvc 配置中的內容協商 ContentNegotiationConfigurer
來實現
@SpringBootApplication public class Application implements WebMvcConfigurer{ @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer){ configurer.favorParameter(true) // 禁用accept協商方式,即不關心前端傳的accept值 // .ignoreAcceptHeader(true) // 哪個放在前面,哪個的優先順序就高; 當上面這個accept未禁用時,若請求傳的accept不能覆蓋下面兩種,則會出現406錯誤 .defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) // 根據傳參mediaType來決定返回樣式 .parameterName("mediaType") // 當acceptHeader未禁用時,accept的值與mediaType傳參的值不一致時,以mediaType的傳值為準 // mediaType值可以不傳,為空也行,但是不能是json/xml之外的其他值 .mediaType("json", MediaType.APPLICATION_JSON) .mediaType("xml", MediaType.APPLICATION_XML); } public static void main(String[] args){ SpringApplication.run(Application.class); } }
上面的實現中,添加了很多註釋,先別急;我來逐一進行說明
.parameterName("mediaType") // 當acceptHeader未禁用時,accept的值與mediaType傳參的值不一致時,以mediaType的傳值為準 // mediaType值可以不傳,為空也行,但是不能是json/xml之外的其他值 .mediaType("json", MediaType.APPLICATION_JSON) .mediaType("xml", MediaType.APPLICATION_XML); 複製程式碼</pre> 上面這三行程式碼,主要就是說,現在可以根據傳參 mediaType 來控制返回的型別,我們新增一個介面來驗證一下 <pre class="prettyprint hljs dart">@GetMapping(path = "param") public ResVo<String> params(@RequestParam(name = "mediaType", required = false) String mediaType) { return new ResVo<>(0, "ok", String.format("基於傳參來決定返回型別:%s", mediaType)); }
我們來看下幾個不同的傳參表現
# 返回json格式資料 curl 'http://127.0.0.1:8080/param?mediaType=json' -iv # 返回xml格式資料 curl 'http://127.0.0.1:8080/param?mediaType=xml' -iv # 406錯誤 curl 'http://127.0.0.1:8080/param?mediaType=text' -iv # 走預設的返回型別,json在前,所以返回json格式資料(如果將xml調整到前面,則返回xml格式資料,主要取決於 `.defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)`) curl 'http://127.0.0.1:8080/param'
疑問:若請求頭中傳遞了 Accept 或者介面上定義了 produce,會怎樣?
當指定了 accept 時,並且傳參中指定了 mediaType,則以傳參為準
- 如
accept: application/json,application.xml
, 此時mediaType=json
, 返回 json 格式 - 如
accept: application/json
, 此時mediaTyep=xml
, 返回 xml 格式 - 如
accept: text/html
,此時mediaType=xml
,此時返回的也是 xml 格式 - 如
accept: text/html
,此時mediaType
不傳時 ,因為無法處理text/html
型別,所以會出現 406 - 如
accept: application/xml
, 但是mediaType
不傳,雖然預設優先是 json,此時返回的也是 xml 格式,與請求頭希望的保持一致
但是若傳參與 produce 衝突了,那麼就直接 406 異常,不會選擇 mediaType 設定的型別
- 如
produce = applicatin/json
, 但是mediaType=xml
,此時就會喜提 406
細心的小夥伴可能發現了上面的配置中,註釋了一行 .ignoreAcceptHeader(true)
,當我們把它開啟之後,前面說的 Accept 請求頭可以隨意傳,我們完全不 care,當做沒有傳這個引數進行處理即可開
4.小結
本文介紹了三種方式,控制介面返回資料型別
方式一
介面上定義 produce, 如 @GetMapping(path = "p2", produces = {"application/xml", "application/json"})
注意 produces 屬性值是有序的,即先定義的優先順序更高;當一個請求可以同時接受 xml/json 格式資料時,上面這個定義會確保這個介面現有返回 xml 格式資料
方式二
藉助標準的請求頭 accept,控制希望返回的資料型別;但是需要注意的時,使用這種方式時,要求後端不能設定 ContentNegotiationConfigurer.ignoreAcceptHeader(true)
在實際使用這種方式的時候,客戶端需要額外注意,Accept 請求頭中定義的 MediaType 的順序,是優於後端定義的 produces 順序的,因此使用者需要將自己實際希望接受的資料型別放在前面,或者乾脆就只設置一個
方式三
藉助 ContentNegotiationConfigurer
實現通過請求引數來決定返回型別,常見的配置方式形如
configurer.favorParameter(true) // 禁用accept協商方式,即不關心前端傳的accept值 // .ignoreAcceptHeader(true) // 哪個放在前面,哪個的優先順序就高; 當上面這個accept未禁用時,若請求傳的accept不能覆蓋下面兩種,則會出現406錯誤 .defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML) // 根據傳參mediaType來決定返回樣式 .parameterName("mediaType") // 當acceptHeader未禁用時,accept的值與mediaType傳參的值不一致時,以mediaType的傳值為準 // mediaType值可以不傳,為空也行,但是不能是json/xml之外的其他值 .mediaType("json", MediaType.APPLICATION_JSON) .mediaType("xml", MediaType.APPLICATION_XML);
即新增這個設定之後,最終的表現為:
produce
注意注意:當配置中忽略了 AcceptHeader 時, .ignoreAcceptHeader(true)
,上面第三條作廢
- 設計模式之狀態模式
- 如何實現資料庫讀一致性
- 我是怎麼入行做風控的
- C 11精要:部分語言特性
- 吳恩達來信:人工智慧領域的求職小 tips
- EasyCV帶你復現更好更快的自監督演算法-FastConvMAE
- 某車聯網App 通訊協議加密分析(四) Trace Code
- 帶你瞭解CANN的目標檢測與識別一站式方案
- EasyNLP玩轉文字摘要(新聞標題)生成
- PostgreSQL邏輯複製解密
- 基於 CoreDNS 和 K8s 構建雲原生場景下的企業級 DNS
- 迴圈神經網路(RNN)可是在語音識別、自然語言處理等其他領域中引起了變革!
- 技術分享| 分散式系統中服務註冊發現元件的原理及比較
- 利用谷歌地圖採集外貿客戶的電話和手機號碼
- 跟我學Python影象處理丨關於影象金字塔的影象向下取樣和向上取樣
- 帶你掌握如何使用CANN 運算元ST測試工具msopst
- 一招教你如何高效批量匯入與更新資料
- 一步步搞懂MySQL元資料鎖(MDL)
- 你知道如何用 PHP 實現多程序嗎?
- KubeSphere 閘道器的設計與實現(解讀)