小工具      在线工具  汉语词典  css  js  c++  java

Spring之RequestBody的使用姿势小结

Java 额外说明

收录于:43天前

SpringMVC中处理请求参数的方式有多种,常见的有以下几种:

  • 根据 HttpServletRequest 对象获取
  • 根据 @PathVariable 注解获取url参数
  • 根据 @RequestParam 注解获取请求参数
  • 根据Bean方法获取请求参数
  • 根据 @ModelAttribute 注解获取请求参数

对上面几种方式有兴趣的可以看一下这篇博文: SpringMVC中如何获取请求参数

除了上面的几种方式之外,还有一种 @RequestBody 的使用方式,本文则主要介绍这种传参的使用姿势和相关注意事项

一、使用姿势

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注解常用于处理content-type不是默认application/x-www-form-urlcoded编码的内容,如:application/json或application/xml等。一般来说,常用来处理 application/json 类型。

A。内容类型定义

在进入下一步之前,有必要说一下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/xhtml+xml:XHTML格式
  • application/xml:XML数据格式
  • application/atom+xml:Atom XML聚合格式
  • application/json:JSON数据格式
  • 申请/pdf:pdf 格式
  • application/msword:Word文档格式
  • application/octet-stream:二进制流数据(如普通文件下载)
  • application/x-www-form-urlencoded :

    默认encType中,form表单数据被编码成key/value格式发送到服务器(表单默认提交数据格式)

b.内容类型示例描述

以上是基本的定义和取值。下面结合例子对几种典型方法进行说明。

  • 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请求时,浏览器用application/x-www-form-urlencoded方式,将表单数据转换成一个字符串(key1=value1&key2=value2...)拼接到url上,这就是我们常见的url带请求参数的情况

邮寄表格

当发起post请求时,如果没有传输文件,浏览器也会将form表单数据封装成k=v的结果,扔到http body中。以开源中文博客提交的表单为例。上传典型的帖子表单。数据以form data的形式组装起来,是一个kv结构。

post

如果有传文件的场景,Content-Type类型会升级为multipart/form-data,这一块不详细展开,后面有机会再说

发布 json 字符串

除了前面的post表单的方法之外,还有一个我们常见的方法,就是把所有的表单数据放在一个大的json字符串中,然后丢给后端。这里还有一个电子商务平台上的在线示例。产品发布,截图如下

IMAGE

请注意上面的请求负载。是一大串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'

IMAGE

因此使用姿势需要显示请求头并更改参数。

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

返回结果如下

IMAGE

3、注意事项

A。内容类型显示规范

根据前面的说明,可以知道 @RequestBody 这个注解的使用,使得REST接口接收的不再content-type为application/x-www-form-urlencoded的请求, 反而需要显示指定为application/json

b.请求方式

RequestBody支持GET方法吗?之前使用post来提交参数。如果改成GET会怎样?

卷曲测试方法

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

对应的后端调试截图如下。发现使用GET方式没有问题,仍然可以获取到参数。

IMAGE

换成著名的POSTMAN来测试

使用post方法请求时,截图如下。主要是修改header的content-type,然后将json字符串格式的请求添加到body中。

IMAGE

但是改成get后,body直接变灰,即不支持在get请求时提交body数据。

IMAGE

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参数,演示如下

请求网址是

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

对应的调试截图如下。 url参数可以获取,但RequestBody参数不能获取。

IMAGE

首先声明一下,以下分析纯属个人推论,没有看源码。如有疑问,还请各位被误导的朋友们表示歉意。也希望了解这一点的朋友能够批评指正。

从传文件的思路出发,前端传文件给后端时,后端是基于流的方式,将上传的二进制流,写入到`MultipartFile`;而二进制流读完之后,没法再重复的读
RequestBody可能也是这么个逻辑,首先是从HttpServletRequest的Reader流中读取body参数并封装到上面的req对象,而不会像url参数一样,写回到`javax.servlet.ServletRequest#getParameterMap`
复制代码

对上面的猜测做一个小验证,改成直接从HttpServletRequest的Reader流中获取请求体参数。

@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);
}
复制代码

验证如下

image

其实此时,有一个有趣的点引起了我的好奇,那就是HttpServletRequest这个东西在Spring容器中是如何工作的。以后有机会再讲,这里就不展开了……

4. 总结

  • ReuqestBody 主要是处理json串格式的请求参数,要求使用方指定header content-type:application/json
  • RequestBody通常要求调用者使用post请求
  • RequsetBody参数,不会放在HttpServletRequest的Map中,因此没法通过javax.servlet.ServletRequest#getParameter获取


链接:https://juejin.im/post/5b5efff0e51d45198469acea
 

. . .

相关推荐

额外说明

由浅入深学习Spring AOP

代理模式学习 什么是AOP 面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 Spring Aop动态代理 JDK动态代理:InvocationHandler、Proxy CGLIB动态代理:MethodIntercept

额外说明

SQL SERVER数据库日志已满,如何清除数据库日志

 SQL SERVER 数据库日志占用很大的空间 ,如果长时间不清除,就会影响数据存储。 一、工具原料 sql server  二 方法、步骤 1、打开sql  server 数据库 在菜单中选择新建查询 2、选择master数据库 3、在sql执行窗口

额外说明

MySQL学习

MySQL 一、MySQL安装和卸载 二、MySQL语法 1.数据库操作 2.修改数据库 3.DML语言 4.DQL语言 5.MySQL函数 6.事务和索引 一、MySQL安装和卸载 详细步骤请见博客资源中的详细步骤 详细安装教程 完全卸载教程 1.安装

额外说明

App后台开发运维——架构设计

QQ 1285575001 Wechat M010527 技术交流 QQ群599020441 纪年科技aming 1.设计app架构 1.梳理app业务流程 2.整理业务流程可能遇到的问题 3.根据问题,探讨可执行的解决方案 4. app后台 初步架构

额外说明

子对话框在按下ESC和Enter键窗口退出的问题

        将对话框作为子窗口,一定要记得将该对话框的属性设定为Child,不能使用Popup属性。之前在编写代码过程中,因为没有将对话框属性设定为Child,导致界面排列布局出现异常,没有按预定的效果显示。        当对话框作为子对话框时,比

额外说明

course2610_lab12 多线程中共享变量的操作

1. 多线程时, 共享变量加一 #include "stdio.h" #include "stdlib.h" #include "pthread.h" #include "errno.h" #include "unistd.h" // 定义线程数目

额外说明

linux salt命令 -e,linux 下 Salt 命令的疑难杂症

前言 今天运维同事在使用 salt '*' test.ping 两台机器上的执行时间完全不同。正常的为: [root@salt_master ~]# time salt-key -L Accepted Keys: 213 217 minion213 Un

额外说明

NPDP日常练习题

1.任何新产品开发流程的一个主要目的是降低不确定性,新产品开发流程的哪个阶段对降低产品失败的风险是最重要的? A.产品上市及上市后的评估 B.扩大产品生产和上市的规模 C.初步的概念开发和商业分析 D.跳过概念评估工作,迅速进入 【答案】C 【解析】参考

额外说明

在HTML 页面中如何显示带圈圈的数字

数字外面有个圈圈, 或者圈圈里有反底色显示,效果类似: 带圈圈的数字示例: ① 反色圈圈数字示例: ❶ 要实现上面的效果,使用以下技术都可以达成: 方式1,. 使用图片 <image> 方式2 ,使用特殊的字体, 比如WingDing系列 方式3, 使用

额外说明

系统缺失msdatgrd.ocx文件导致程序出现错误怎么办?

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个msdatgrd.ocx文件

ads via 小工具