180730-Spring之RequestBody的使用姿勢小結

來源:互聯網
上載者:User

標籤:升級   寫法   seo   能力   type屬性   ges   atom   基於   輸出   

Spring之RequestBody的使用姿勢小結

SpringMVC中處理請求參數有好幾種不同的方式,如我們常見的下面幾種

  • 根據 HttpServletRequest 對象擷取
  • 根據 @PathVariable 註解擷取url參數
  • 根據 @RequestParam 註解擷取請求參數
  • 根據Bean的方式擷取請求參數
  • 根據 @ModelAttribute 註解擷取請求參數

對上面幾種方式有興趣的可以看一下這篇博文: SpringMVC之請求參數的擷取方式

除了上面的幾種方式之外,還有一種 @RequestBody 的使用方式,本文則主要介紹這種傳參的使用姿勢和相關注意事項

I. 使用姿勢1. 服務介面

藉助Spring架構,使用@RequestBody並沒有什麼難度,很簡單的就可以寫一個使用case出來,如下

@Slf4j@RestControllerpublic class ReqBodyController {    @Data    @NoArgsConstructor    @AllArgsConstructor    public static class Req {        private String key;        private Integer size;    }    @RequestMapping(value = "/body", method = {RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS})    public BaseRsp body(@RequestBody Req req) {        log.info("req: {}", req);        return new BaseRsp<>(req);    }}

看上面的實現,和我們通常的寫法並無差別,無非是將以前的 @RequsetParam 註解換成 @RequsetBody 註解,而且這個註解內部只有一個filed,比RequsetParam還少

@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestBody {    // 預設參數必須存在,否則會拋一個異常    boolean required() default true; }

看到上面的實現,估計也可以猜出,這個註解對於後端而言,寫沒啥問題,關鍵是如何用(具體來講是如何給前端用)

2. 介面調用

上面寫完了,接下來的重點就是如何使用了,在使用之前,有必要瞭解下 RequestBody 這個註解出現的原有以及應用情境(換句話說它和RequestParam有什麼區別,為什麼要單獨的搞一個這個東西出來)

RequestBody

@requestBody註解常用來處理content-type不是預設的application/x-www-form-urlcoded編碼的內容,比如說:application/json或者是application/xml等。一般情況下來說常用其來處理application/json類型。

a. content-type定義

在進入下一步之前,有必要說一下Content-Type這個http要求標頭的作用了,下面一段來自其他博文,原文連結見最後

MediaType,即是Internet Media Type,互連網媒體類型;也叫做MIME類型,在Http協議訊息頭中,使用Content-Type來表示具體請求中的媒體類型資訊。

常見媒體格式如下:

  • text/html : HTML格式
  • text/plain :純文字格式
  • text/xml : XML格式
  • image/gif :gif圖片格式
  • image/jpeg :jpg圖片格式
  • image/png:png圖片格式

以application開頭的媒體格式類型:

  • application/xhtml+xml :XHTML格式
  • application/xml : XML資料格式
  • application/atom+xml :Atom XML彙總格式
  • application/json : JSON資料格式
  • application/pdf :pdf格式
  • application/msword : Word文檔格式
  • application/octet-stream : 二進位流資料(如常見的檔案下載)
  • application/x-www-form-urlencoded :
b. content-type 執行個體說明

上面算是基本定義和取值,下面結合執行個體對典型的幾種方式進行說明

  • application/x-www-form-urlencoded:資料被編碼為成對的名稱和數值。這是標準的編碼格式。
  • multipart/form-data: 資料被編碼為一條訊息,頁上的每個控制項對應訊息中的一個部分。
  • text/plain: 資料以純文字形式(text/json/xml/html)進行編碼,其中不含任何控制項或格式字元

對於前端使用而言,form表單的enctype屬性為編碼方式,常用有兩種:application/x-www-form-urlencodedmultipart/form-data,預設為application/x-www-form-urlencoded

Get請求

發起Get請求時,瀏覽器用application/x-www-form-urlencoded方式,將表單資料轉換成一個字串(key1=value1&key2=value2...)拼接到url上,這就是我們常見的url帶請求參數的情況

Post表單

發起post請求時,如果沒有傳檔案,瀏覽器也是將form表單的資料封裝成k=v的結果丟到http body中,拿開源中國的部落格提交的表單為例,一個典型的post表單,上傳的資料拼裝在form data中,為kv結構

如果有傳檔案的情境,Content-Type類型會升級為multipart/form-data,這一塊不詳細展開,後面有機會再說

Post json串

post表單除了前面一種方式之外,還有一種也是我們常見的,就是講所有的表單資料放在一個大的json串中,然後丟給後端,這裡也有一個線上的執行個體,某電商平台的商品發表,如下

注意看上面的Request Payload,是一個大的json串,和前面差別明顯

c. RequestBody請求

根據RequestBody的定義,要想訪問前面定義的那個介面,使用傳統的表單傳遞方式是不行的,curl命令測試如下

curl -X POST -d ‘key=haha&size=123‘ http://127.0.0.1:19533/body

後端對應的輸出如下(拋了一個異常,表示@RequestBody註解修飾rest介面,不支援 Content type ‘application/x-www-form-urlencoded;charset=UTF-8‘

因此使用姿勢需要顯示添加要求標頭,傳參也改變一下

curl -l -H "Content-type: application/json" -X GET -d ‘{"key": "!23", "size": 10}‘ http://127.0.0.1:19533/body

返回結果如下

3. 注意事項a. content-type顯示指定

根據前面的說明,可以知道 @RequestBody 這個註解的使用,使得REST介面接收的不再content-type為application/x-www-form-urlencoded的請求, 反而需要顯示指定為application/json

b. 要求方法

RequestBody支援GET方法嗎?前面都是採用post提交參數,如果改成GET會怎樣?

curl測試方式

curl -l -H "Content-type: application/json" -X GET -d ‘{"key": "!23", "size": 10}‘ http://127.0.0.1:19533/body\?key\=app

對應的後端debug如下,發現使用GET方式,並沒有問題,依然可以擷取到參數

換成大名鼎鼎的POSTMAN來測試

使用post方法請求時,如下,主要就是修改header的content-type,然後在body中添加json串格式的請求

然而改成get之後,body都直接灰掉了,也就是它不支援在get請求時,提交Body資料

url請求方式

接下來直接換成url的請求方式,看是否直接支援get請求

http://127.0.0.1:19533/body?{"key": "!23", "size": 10}

瀏覽器中輸入時,伺服器400, 換成curl方式請求,拋的是缺少RequestBody的異常,也就是說,將json串拼接到url中貌似不行(也有可能是我的使用姿勢不對。。。)

小結

  • 到這裡小結一下,使用RequestBody擷取參數時,還是老老實實的選擇POST方法比較合適,至於原因,跟福士,隨主流,跟著大家的習慣走比較好
c. 參數擷取

這個主要就是後端編寫介面時,擷取RequestBody參數的問題了,通過測試,發現在HttpServletRequest參數中,居然拿不到提交的RequestBody參數,示範如下

請求url為

curl -l -H "Content-type: application/json" -X POST -d ‘{"key": "!23", "size": 10}‘ http://127.0.0.1:19533/body\?url\=ddd

對應的debug如下,url參數可以拿到,RequestBody參數沒有

首先聲明,下面的這段分析,沒有看源碼,純屬於個人推斷,如有問題,對被誤導的朋友表示歉意,也希望對此有瞭解的朋友,多多批評指正

從傳檔案的思路出發,前端傳檔案給後端時,後端是基於流的方式,將上傳的二進位流,寫入到`MultipartFile`;而二進位流讀完之後,沒法再重複的讀RequestBody可能也是這麼個邏輯,首先是從HttpServletRequest的Reader流中讀取body參數並封裝到上面的req對象,而不會像url參數一樣,寫回到`javax.servlet.ServletRequest#getParameterMap`

對上面的猜測做一個小小的驗證,改成直接從HttpServletRequest的Reader流中擷取請求body參數

@RequestMapping(value = "/body", method = {RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS})public BaseRsp body(HttpServletRequest request) throws IOException {    BufferedReader reader = request.getReader();    StringBuilder builder = new StringBuilder();    String line = reader.readLine();    while (line != null) {        builder.append(line);        line = reader.readLine();    }    reader.close();    String reqBody = builder.toString();    Req req = JSON.parseObject(reqBody, Req.class);    log.info("req: {}, request: {}", req, request.getParameterMap());    return new BaseRsp<>(req);}

驗證如下

其實到這裡,有個有意思的地方已經引起了我的好奇,那就是在Spring容器中HttpServletRequest這個東西,是怎麼運轉的,後面有機會再聊,此處不展開...

4. 小結
  • ReuqestBody 主要是處理json串格式的請求參數,要求使用方指定header content-type:application/json
  • RequestBody 通常要求調用方使用post請求
  • RequsetBody參數,不會放在HttpServletRequest的Map中,因此沒法通過javax.servlet.ServletRequest#getParameter擷取
II. 其他0. 參考
  • SpringMVC之請求參數的擷取方式
  • Http中Content-Type的詳解
1. 一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

2. 聲明

盡信書則不如,已上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

  • 微博地址: 小灰灰Blog
  • QQ: 一灰灰/3302797840
3. 掃描關注

小灰灰Blog&公眾號

知識星球

180730-Spring之RequestBody的使用姿勢小結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.