Spring中的@RequestBody注解与常规的HTTP方法的传值方式
Posted 今夕是何夕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
|
POST HTTP/1.1
|
注意:使用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
|
POST / HTTP/1.1
|
上面的请求中,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中的请求示例:
?
注意:当非post请求的请求体中也含有JSON字符串时,依旧可以使用@RequestBody拿到请求体中的数据。
Postman测试
下面使用Postman来测试一下什么时候要加@Requestbody,什么时候不用加@RequestBody注解:
首先是Controller中的代码,有个方法,一个使用了@RequestBody注解,一个没有使用:
1
|
|
当请求中的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对象的方式去接收,否则会接收不到参数
参考地址:
以上是关于Spring中的@RequestBody注解与常规的HTTP方法的传值方式的主要内容,如果未能解决你的问题,请参考以下文章
Spring中的@RequestBody和@ResponseBody注解
自定义spring参数注解 - 打破@RequestBody单体限制
Spring中的注解 @RequestBody和@ResponseBody的使用和区别