Spring MVC 教程-@RequestBody原理解密

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

本文将介绍@RequestBody注解常见的一些用法和原理,这个注解日常用到的特别多。

1、预备知识

  1.  接口测试利器 HTTP Client
  2. 参数解析器HandlerMethodArgumentResolver解密

2、@RequestBody介绍

标注在接口的参数上,用来获取HTTP请求body中的值,下面通过案例列出常见的用法。

    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RequestBody {
    
    	/**
    	 * body是不是必须的,默认为true,若不传body,会有异常;若为false,这body可不传
    	 */
    	boolean required() default true;
    
    }

 

3、案例1:使用字符串接收body中的数据

3.1、接口代码

注意方法的参数,使用@RequestBody标注,参数类型是String,表示以字符串的方式接收body的数据。

    @RequestMapping("/requestbody/test1")
    public String test1(@RequestBody String body) {
        System.out.println("body:" + body);
        return "ok";
    }

下面来模拟发送5种格式的数据,然后看控制台的输出。

3.2、用例1:发送纯文本数据

Content-Type用来指定客户端发送的数据的类型。

    ### 发送纯文本
    POST http://localhost:8080/chat18/requestbody/test1
    Content-Type: text/plain
    
    这里是body部分,欢迎访问我的博客:itsoku.com,上面有更多系列文章

运行,接口内部控制台输出

    body:这里是body部分,欢迎访问我的博客:itsoku.com,上面有更多系列文章

3.3、用例2:发送表单数据,相当于提交表单

Content-Type: application/x-www-form-urlencoded 相当于页面中提交表单,表单中的所有元素会以name=value&name=value的方式拼接起来,然后在进行urlencoded,之后丢在body中发送。

    ### 发送表单数据,相当于提交表单
    POST http://localhost:8080/chat18/requestbody/test1
    Content-Type: application/x-www-form-urlencoded
    
    name=路人&blogs=itsoku.com

运行输出如下,可以看出来是乱码的格式,是由于被中文被urlencoded编码了。

    body:name=%E8%B7%AF%E4%BA%BA&blogs=itsoku.com

3.4、用例3:发送xml数据

    ### 发送xml数据
    POST http://localhost:8080/chat18/requestbody/test1
    Content-Type: text/xml
    
    <CourseList>
        <Course>Java高并发系列</Course>
        <Course>MyBatis系列</Course>
        <Course>MySQL系列</Course>
        <Course>Spring高手系列</Course>
        <Course>分布式事务高手系列</Course>
    </CourseList>

运行,控制台输出

    body:<CourseList>
        <Course>Java高并发系列</Course>
        <Course>MyBatis系列</Course>
        <Course>MySQL系列</Course>
        <Course>Spring高手系列</Course>
        <Course>分布式事务高手系列</Course>
    </CourseList>

3.5、用例4:发送json数据

    ### 发送json数据
    POST http://localhost:8080/chat18/requestbody/test1
    Content-Type: application/json;charset=UTF-8
    
    {
      "blog": "itsoku.com",
      "course": [
        "Spring高手系列",
        "MySQL系列",
        "高并发系列"
      ]
    }

运行,控制台输出

    body:{
      "blog": "itsoku.com",
      "course": [
        "Spring高手系列",
        "MySQL系列",
        "高并发系列"
      ]
    }

从上面可以看出,接口参数body的值为http请求body中的原始数据。

4、案例2:使用对象接收json格式的数据

4.1、用法

发送json格式的数据,这种用到的比较多,http请求发送这种数据,有3点要求:

  1. Content-Type的值需要为:application/json;charset=UTF-8,告诉服务器端客户端body中的数据是json格式 & UTF-8编码
  2. body中数据为json格式
  3. 接口端用对象接收,参数使用@RequestBody标注

4.2、接口代码

    @RequestMapping("/requestbody/test2")
    public String test2(@RequestBody User user) {
        System.out.println("user:" + user);
        return "ok";
    }

User类

    public class User {
        private String name;
        private Integer age;
        private List<String> skills;
    
        //省略get、set
    
        @Override
        public String toString() {
            return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skills=" + skills +
                '}';
        }
    }

4.3、调用接口

重点注意了,头中需要加上Content-Type: application/json

    ### 发送json数据,后端用对象接收
    POST http://localhost:8080/chat18/requestbody/test2
    Content-Type: application/json;charset=UTF-8
    
    {
      "name": "路人",
      "age": 35,
      "skills": [
        "高并发",
        "Spring",
        "分布式事务",
        "MQ",
        "MySQL"
      ]
    }

4.4、控制台输出

    user:User{name='路人', age=35, skills=[高并发, Spring, 分布式事务, MQ, MySQL]}

 

5、案例3:使用Resource资源对象接收

5.1、用法

有时候,我们想以流的方式接收body中的数据,那么可以参考下面的写法,参数类型为[ByteArrayResource,InputStreamResource]这2种类型即可,第一种类型获取的是一个字节数组,第二个是一个InputStream输入流。

比如我们需要快速上传文件到阿里云,那么接口接收到客户端的流之后,直接将流转发到oss,效率更高。

    /**
     * 参数为如果为 org.springframework.core.io.Resource 类型,
     * 则只能为Resource的[ByteArrayResource,InputStreamResource]这2种子类型:
     *
     * @param body
     * @return
     * @throws IOException
     */
    @RequestMapping("/requestbody/test3")
    public String test3(@RequestBody InputStreamResource body) throws IOException {
        String content = IOUtils.toString(body.getInputStream(), "UTF-8");
        System.out.println("content:" + content);
        return "ok";
    }

5.2、调用接口

    ### 后端使用Resource接收数据
    POST http://localhost:8080/chat18/requestbody/test3
    Content-Type: text/plain;charset=UTF-8
    
    后端使用Resource接收数据

5.3、控制台输出

    content:后端使用Resource接收数据

 

6、案例4:以字节数组接受数据

6.1、代码

    /**
     * 使用字节数组接收
     *
     * @param bodyBytes
     * @return
     */
    @RequestMapping("/requestbody/test4")
    public String test4(@RequestBody byte[] bodyBytes) {
        System.out.println("body长度(bytes):" + bodyBytes.length);
        System.out.println("body内容:" + new String(bodyBytes));
        return "ok";
    }

6.2、调用接口

    ### 后端使用字节数组接收数据
    POST http://localhost:8080/chat18/requestbody/test4
    Content-Type: text/plain;charset=UTF-8
    
    itsoku.com

6.3、控制台输出

    body长度(bytes):10
    body内容:itsoku.com

 

7、案例5:使用HttpEntity接收数据

7.1、HttpEntity:含有头和body信息

如果想同时拿到头和body的数据,可以使用,org.springframework.http.HttpEntity来接收数据,这个类中包含了头和body的信息,body是一个泛型,http请求的数据会被转换为body对应的T类型。

7.2、案例代码

注意:HttpEntity类型的参数不要用@RequestBody标注。

    @RequestMapping("/requestbody/test5")
    public String test5(HttpEntity<User> httpEntity) {
        //header信息
        HttpHeaders headers = httpEntity.getHeaders();
        System.out.println("headers:" + headers);
        //body中的内容会自动转换为HttpEntity中泛型指定的类型
        User user = httpEntity.getBody();
        System.out.println("body:" + user);
        return "ok";
    }

7.3、调用案例接口

    ### 发送json数据,后端用HttpEntity<User>接收
    POST http://localhost:8080/chat18/requestbody/test5
    Content-Type: application/json;charset=UTF-8
    
    {
      "name": "路人",
      "age": 35,
      "skills": [
        "高并发",
        "Spring",
        "分布式事务",
        "MQ",
        "MySQL"
      ]
    }

7.4、控制台输出

    headers:[content-type:"application/json;charset=UTF-8", content-length:"130", host:"localhost:8080", connection:"Keep-Alive", user-agent:"Apache-HttpClient/4.5.12 (Java/11.0.10)", accept-encoding:"gzip,deflate"]
    body:User{name='路人', age=35, skills=[高并发, Spring, 分布式事务, MQ, MySQL]}

 

8、案例6:使用RequestEntity接受数据

8.1、RequestEntity:包含更多请求信息(头、method、url,body)

RequestEntity的用法和案例5中的HttpEntity用法类似,RequestEntity继承了HttpEntity,包含了更多的信息,比RequestEntity多了2个http请求信息(method和url)

8.2、案例代码

    @RequestMapping("/requestbody/test6")
    public String test6(RequestEntity<User> requestEntity) {
        //请求方式
        HttpMethod method = requestEntity.getMethod();
        System.out.println("method:" + method);
        //请求地址
        URI url = requestEntity.getUrl();
        System.out.println("url:" + url);
        //body的类型,即RequestEntity后面尖括号中的类型
        Type type = requestEntity.getType();
        System.out.println("body的类型,即RequestEntity后面尖括号中的类型:" + type);
        //header信息
        HttpHeaders headers = requestEntity.getHeaders();
        System.out.println("headers:" + headers);
        //body中的内容会自动转换为HttpEntity中泛型指定的类型
        User user = requestEntity.getBody();
        System.out.println("body:" + user);
        return "ok";
    }

8.3、调用案例接口

    ### 发送json数据,后端用对象接收
    POST http://localhost:8080/chat18/requestbody/test6
    Content-Type: application/json;charset=UTF-8
    
    {
      "name": "路人",
      "age": 35,
      "skills": [
        "高并发",
        "Spring",
        "分布式事务",
        "MQ",
        "MySQL"
      ]
    }

8.4、控制台输出

    method:POST
    url:http://localhost:8080/chat18/requestbody/test6
    body的类型,即RequestEntity后面尖括号中的类型:class com.javacode2018.springmvc.chat18.controller.RequestBodyController$User
    headers:[content-type:"application/json;charset=UTF-8", content-length:"130", host:"localhost:8080", connection:"Keep-Alive", user-agent:"Apache-HttpClient/4.5.12 (Java/11.0.10)", accept-encoding:"gzip,deflate"]
    body:User{name='路人', age=35, skills=[高并发, Spring, 分布式事务, MQ, MySQL]}

9、@RequestBody还可以如何使用呢?

这里留给大家去研究,大家在运行一下案例1中的用例1

    ### 发送纯文本
    POST http://localhost:8080/chat18/requestbody/test1
    Content-Type: text/plain
    
    这里是body部分,欢迎访问我的博客:itsoku.com,上面有更多系列文章

控制台有更详细的输出如下,注意里面的RequestResponseBodyMethodProcessor,这个就是@ReqeustBody类型的参数处理器,@ReqeustBody标注的参数的值都是有这个类来解析请求得到的,大家可以去看看这个类的代码,debug一番,就知道@ReqeustBody还有那些更炫的用法了。

    23:17:05.595 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - POST "/chat18/requestbody/test1", parameters={}
    23:17:05.595 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.javacode2018.springmvc.chat18.controller.RequestBodyController#test1(String)
    23:17:05.596 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Read "text/plain;charset=UTF-8" to ["这里是body部分,欢迎访问我的博客:itsoku.com,上面有更多系列文章"]
    body:这里是body部分,欢迎访问我的博客:itsoku.com,上面有更多系列文章
    23:17:05.597 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Using 'text/plain', given [*/*] and supported [text/plain, */*, application/json, application/*+json]
    23:17:05.597 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Writing ["ok"]
    23:17:05.598 [http-nio-8080-exec-9] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK

主要有5行日志,每行日志这里做一下解释

第1行:接收到了请求,请求的信息(url,参数)

第2行:找到了能够处理请求的方法,即RequestBodyController#test1(String)方法可以处理当前请求

第3行:参数解析器,@RequestBody对应的是RequestResponseBodyMethodProcessor

第4行:接口中System.out.println输出的内容

第5行:返回值处理器,这个以后会有专题讲解

10、@RequestBody原理

@RequestBody标注的参数取值是由RequestResponseBodyMethodProcessor#resolveArgument方法处理的,可以去看源码。

11、代码位置及说明

11.1、git地址

    https://gitee.com/javacode2018/springmvc-series

11.2、本文案例代码结构说明

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值