180730-Spring之RequestBody的使用姿势小结
Spring之RequestBody的使用姿势小结
SpringMVC中处理请求参数有好几种不同的方式,如我们常见的下面几种
- 根据
HttpServletRequest
对象获取 - 根据
@PathVariable
注解获取url参数 - 根据
@RequestParam
注解获取请求参数 - 根据Bean的方式获取请求参数
- 根据
@ModelAttribute
注解获取请求参数
对上面几种方式有兴趣的可以看一下这篇博文: SpringMVC之请求参数的获取方式
除了上面的几种方式之外,还有一种 @RequestBody
的使用方式,本文则主要介绍这种传参的使用姿势和相关注意事项
I. 使用姿势
1. 服务接口
借助Spring框架,使用@RequestBody
并没有什么难度,很简单的就可以写一个使用case出来,如下
@Slf4j
@RestController
public 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)
@Documented
public @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 : 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
b. content-type 实例说明
上面算是基本定义和取值,下面结合实例对典型的几种方式进行说明
- application/x-www-form-urlencoded:数据被编码为名称/值对。这是标准的编码格式。
- multipart/form-data: 数据被编码为一条消息,页上的每个控件对应消息中的一个部分。
- text/plain: 数据以纯文本形式(text/json/xml/html)进行编码,其中不含任何控件或格式字符
对于前端使用而言,form表单的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded
和multipart/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. 参考
1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 扫描关注
小灰灰Blog&公众号
知识星球
180730-Spring之RequestBody的使用姿势小结的更多相关文章
- Spring @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
背景 昨天一个人瞎倒腾spring boot,然后遇到了一点问题,所以把这个问题总结一下. 主要讲解request 数据到handler method 参数数据的绑定,所用到的注解和什么情形下使用. ...
- spring mvc中的拦截器小结 .
在spring mvc中,拦截器其实比较简单了,下面简单小结并demo下. preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如我们上一章的Control ...
- SpringBoot基础篇AOP之基本使用姿势小结
一般来讲,谈到Spring的特性,绕不过去的就是DI(依赖注入)和AOP(切面),在将bean的系列中,说了DI的多种使用姿势:接下来看一下AOP的玩法 <!-- more --> I. ...
- Spring MVC @RequestBody自动转JSON HTTP415错误解决方法
转自:http://blog.csdn.net/tiantiandjava/article/details/46125141 项目中想用@RequestBody直接接收json串转成对象 网上查了使用 ...
- SpringBoot Controller 中使用多个@RequestBody的正确姿势
最近遇到Controller中需要多个@RequestBody的情况,但是发现并不支持这种写法, 这样导致 1.单个字符串等包装类型都要写一个对象才可以用@RequestBody接收: 2.多个对象需 ...
- Spring Boot 日志各种使用姿势,是时候捋清楚了!
@ 目录 1. Java 日志概览 1.1 总体概览 1.2 日志级别 1.3 综合对比 1.4 最佳实践 2. Spring Boot 日志实现 2.1 Spring Boot 日志配置 2.2 L ...
- Spring单例与线程安全小结
一.Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方. 单例模式的意思就是只有一个实例.单例模式确 ...
- spring中RequestBody注解接收参数时用JSONField转参数名无效问题
问题: 在springboot项目中使用@RequestBody注解接收post请求中body里的json参数的情况.即: @RequestMapping(value = "/get-use ...
- spring boot @RequestBody数据传递及解析
@RequestBody需要接的参数是一个string化的json @RequestBody,要读取的数据在请求体里,所以要发post请求,还要将Content-Type设置为application/ ...
随机推荐
- sql-case when 条件1 then 取值1 when 条件2 then 取值2 else 取值3 end
遇到 XXX情况 就 XXX 遇不到就 XXX 结束case when …… then …… else …… end 例如一个3条件取值的字段: case when 条件1 then 取值1 when ...
- CABasicAnimation使用总结
CABasicAnimation使用总结 实例化 使用方法animationWithKeyPath:对 CABasicAnimation进行实例化,并指定Layer的属性作为关键路径进行注册. //围 ...
- navicat mysql 存储过程
存储过程如同一门程序设计语言,同样包含了数据类型.流程控制.输入和输出和它自己的函数库. 一.基本数据类型:略 二.变量: 自定义变量:DECLARE a INT ; SET a=100; 可用以 ...
- MongoDB的入门
MongoDB mongodb是非关系型数据库 对于关系型数据库,存储数据的时候需要提前建表建库,随着数据的复杂度越来越高,所建的表的数量也越来越多:但是非关系型却不需要 mongodb的基本的命令的 ...
- PHP && ,and ,||,or 的区别
PHP中的逻辑“与”运算有两种形式:AND 和 &&,同样“或”运算也有OR和||两种形式. 如果是单独两个表达式参加的运算,两种形式的结果完全相同,例如 $a AND $b和$a & ...
- PHP中使用RabiitMQ---各项参数的使用方法
RabbitMQ在PHP使用,我在这里对RabbitMQ的各项方法和参数进行了一些梳理,有不足的地方还望各位大神指点. 想要使用rabbitMQ消息队列,首先需要安装 php_amqp.dll 扩展 ...
- TCP/IP协议的数据传输过程详解——IP与以太网的包收发操作
MTU:一个网络包的最大长度,以太网中一般是1500字节:(含有头部长度,包括IP头部,TCP头部,不包括MAC头部) MSS:除去头部后,一个网络包所能容纳的TCP的数据的最大长度 下图为TCP/I ...
- Leecode刷题之旅-C语言/python-387 字符串中的第一个唯一字符
/* * @lc app=leetcode.cn id=387 lang=c * * [387] 字符串中的第一个唯一字符 * * https://leetcode-cn.com/problems/f ...
- CRLF注入学习
预备 <CRLF>是换行符,CRLF注入顾名思义就是把换行符写入,那么要把换行符写入到哪里呢?看看下面的http头 可以看到,每一行都包含特定的头部信息,然后以换行为标志写入其他的头部信息 ...
- 通过SSH服务登陆linux服务器(版本RHEL7)
通过SSH服务登陆linux服务器(版本RHEL7) SSH服务概述:是一种能够以安全的方式提供远程登陆的协议,也是目前远程管理linux系统的首选方式.在此之前,我们一般使用FTP或者telnet来 ...