写点什么

重磅来袭:Spring 之 RequestBody 的使用姿势小结

发布于: 2021 年 04 月 22 日
重磅来袭: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 : 中默认的 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 获取

原文链接:https://juejin.cn/post/6844903648858734600

最后,小编还给大家整理了一份面试宝典 有需要的添加小助理vx:mxzFAFAFA来领取
复制代码




发布于: 2021 年 04 月 22 日阅读数: 31
用户头像

领取文章中资料添加小助理vx:mxzFAFAFA 2021.02.05 加入

Java架构大数据每天分享干货!

评论

发布
暂无评论
重磅来袭:Spring之RequestBody的使用姿势小结