【轉】Spring中的@RequestBody注解與常規的HTTP方法的傳值方式


地址:https://super-aviator.github.io/2019/08/30/Spring%E4%B8%AD%E7%9A%84-RequestBody%E6%B3%A8%E8%A7%A3/

上周進行項目開發的時候,發現前端的含有文件的表單數據時后台接收報錯,異常的大致意思是表單類型不支持,我也是有點蒙逼,以前也遇到過這種問題,加上@RequestBody就可以,所以這次我也加上了@RequestBody注解,結果還是報錯,這時前端發消息過來,告訴我不能加@RequestBody,我去掉之后果然好了,好吧。一直沒弄明白什么時候要加@RequestBody什么時候不要加,趁着這個迭代的任務很輕松,學習一下Spring中@RequestBody注解和后端如何接收常用的HTTP方法傳過來的數據,結合PostMan對結果進行測試。

@RequestBody注解

@RequestBody注解常用來處理POST請求,並且content-type不是默認的application/x-www-form-urlcoded編碼的內容,比如說:application/json或者是application/xml等。一般情況下來說常用其來處理application/json類型。

@RequestMapping注解的方法的參數中包含了@RequestBody注解,那么Spring會首先查看請求中的Content-Type頭部,然后根據Content-Type頭部去查找合適的HttpMessageConverter

例如,如果客戶端發送的Spittle數據是JSON表述形式,那 么Content-Type頭部信息可能就會是“application/json”。在 這種情況下,DispatcherServlet會查找能夠將JSON轉換為Java 對象的消息轉換器。如果Jackson 2庫在類路徑中,那 么MappingJackson2HttpMessageConverter將會擔此重任,將 JSON表述轉換為Spittle,然后傳遞到saveSpittle()方法中。 這個方法還使用了@ResponseBody注解,因此方法返回的Spittle 對象將會轉換為某種資源表述,發送給客戶端。

在《Spring 實戰》中,表明了@RequestBody注解的含義和使用方式:用來解析請求體(可能是POST,PUT,DELETE,GET請求)中Content-Type為application/json類型的請求,利用消息轉換器將其轉換為對應的java對象(必須使用VO對象去接收),那么什么類型的消息能夠加上@RequestBody,什么類型的消息不能加呢?

表單類型

MediaType,即是Internet Media Type,互聯網媒體類型;也叫做MIME類型,在Http協議消息頭中,使用Content-Type來表示具體請求中的媒體類型信息。Content-Type頭部后面可以追加;charset=UTF-8指定編碼格式,例如:Content-Type:x-www-from-urlencoded;charset=UTF-8

Content-Type字段表明了請求的請求體類型,可以是如下幾種常見的類型:
常見媒體格式如下:

  • text/html : HTML格式
  • text/plain :純文本格式
  • text/xml : XML格式
  • image/gif :gif圖片格式
  • image/jpeg :jpg圖片格式
  • image/png:png圖片格式
  • multipart/form-data:(體數據被編碼為一條消息,頁上的每個控件對應消息中的一個部分,這個一般文件上傳時用)

以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 : 中默認的encType,form表單數據被編碼為key/value格式發送到服務器(表單默認的提交數據的格式)

下面是常見的表單提交的Content-Type的取值:

application/x-www-form-urlencoded

  • 如果使用的是GET請求,瀏覽器用x-www-form-urlencoded的編碼方式把form數據轉換成一個字串(name1=value1&name2=value2…),然后把這個字串append到url后面,用?分割,加載這個新的url。

  • 如果使用的是POST請求,會采用類似GET的字符串拼接的方式,將拼接的key-value字段放到body里面,而非url的后面,所以POST請求的url長度是沒有限制的,因為拼接的url請求參數都存放在body里面,如下

1
2
3
4
5
6
7
   POST  HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: e8c31d7a-fd52-cbf2-2741-07d73cb1101b

keyword=panda&author=zane

注意:使用application/x-www-form-urlencoded編碼方式的請求會對所有非ASCII的字符使用%HH的方式進行轉換,所以一個非ASCII字符會由三個字符去表示,當非ASCII非常多時,會增加大約三倍的帶寬,這無疑是一種浪費。

multipart/form-data

如果沒有type=file的控件,用默認的application/x-www-form-urlencoded就可以了。 但是如果有type=file的話,Content-Type就會升級使用multipart/form-data類型。瀏覽器會把整個表單以控件為單位分割,並為每個部分加上Content-Disposition(form-data或者file),Content-Type(默認為text/plain),name(控件的name,即字段名)等信息,並加上分割符(boundary),例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: 090df52c-d103-279b-1479-50e6a7fef58b

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="keyword"

panda
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="author"

zane
------WebKitFormBoundary7MA4YWxkTrZu0gW

上面的請求中,keyword鍵對應的值為panda,author鍵對應的值為zane。

首先我們看請求頭的 Content-Type 它除了正常的 multipart/form-data 外還多了一個 boundary ,這個 boundary 的意思和字面意思一樣就是分界線,通過分界線將每個鍵值對用 boundary 分割開來以示區別,這個分界線是特殊選擇出來的,以便該boundary不會出現在任何有效負載中。現在我們看請求體,我們注意到boundary 將鍵值對分割后的每一部分都有 Content-Disposition 字段,實際上該字段的值必須為 form-data 而且后面必須加上 name 指定這部分的鍵名,然后是一行空行,空行之后便是提交數據的內容。 之所以要弄的這么復雜是因為 multipart/form-data 要支持文件上傳。

注意:使用multipart/form-data編碼方式的請求不會對非ASCII字符進行轉碼,所以也就不會有消耗,但是對於簡短的字母數字值(與大多數web表單一樣),添加所有MIME頭的開銷將大大超過更有效的二進制編碼所節省的開銷。

application/json

數據以純文本形式(text/json/xml/html)進行編碼,POST方法使用這種方式會把表單的鍵值對以一個JSON字符串的方式放到HTTP的body里面。例如如下的postman中的請求示例:
?
upload successful

注意:當非post請求的請求體中也含有JSON字符串時,依舊可以使用@RequestBody拿到請求體中的數據。

Postman測試

下面使用Postman來測試一下什么時候要加@Requestbody,什么時候不用加@RequestBody注解:
首先是Controller中的代碼,有個方法,一個使用了@RequestBody注解,一個沒有使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RestController
@RequestMapping("/requestBody")
@Slf4j
public class RequestBodyController {
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
private static class Request{
private String name;
private Integer age;
@JSONField(serialize = false,deserialize = false)
private MultipartFile avatar;
}

@PostMapping("/request")
public HttpEntity<Request> generalRequest(Request request) {
log.info("generalRequest-{}",JSON.toJSONString(request));
return new HttpEntity<>(request);
}

@PostMapping("/requestBody")
public HttpEntity<Request> generalRequestBody(@RequestBody Request request) {
log.info("generalRequest-{}",JSON.toJSONString(request));
return new HttpEntity<>(request);
}
}

當請求中的ContentType分別為一下三種類型時,結果如下:

否加上注解\ContentType x-www-form-urlencoded form-data application/json
不加@RequestBody注解 能接收 能接收 不能接收
加上@RequestBody注解 不能接收 不能接收 能接收

@RequestBody使用總結

@RequestBody用於需要觸發HttpMessageConverter的場景:

  • 當HTTP請求的Content-Type頭部為application/json時,需要加上@RequestBody注解,並使用默認的HttpMessageConverter或者自定義的HttpMessageConverter對請求的body中的json字符串轉換為java對象。
  • 當Content-Type頭部的值為application/x-www-form-urlencoded或者multipart/form-data時,表名此請求是一個常規的表單請求,不能使用@RequestBody注解。

注意:使用@RequestBody注解的參數必須使用VO對象的方式去接收,否則會接收不到參數

參考地址:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM