【SpringBoot接收接口入参的几种方式】

0.引言

1) 传参的几种方式

我们从调用方的视角去看待这个问题,对调用方来说,它在调用接口时有如下的几种传参方式:

  1. Query参数。表现形式是调用方在调用接口时,入参是拼接在接口的URI后面的,如/test_get/requestparam_1?name=wangwu&age=18。这种方式在入参个数比较少GET请求方式中比较常用。
  2. Path参数。这是REST风格的路径参数,入参直接拼接在接口的URI里面,如/test_pathvar/test1/zhangsan/1,其中的zhangsan1就是就是参数。这种方式在REST风格的接口中比较常用。
  3. Body参数。这种方式是把入参放在了请求体当中!它跟前两种入参方式的最大区别,就是:
    1)前两种入参方式它们的入参都是直接体现在了调用接口时候的URI
    2)而当前的这种Body参数方式,它的入参是放在了Body请求体
    而且,Body参数又可以细分成如下的几种方式:
    • application/json 前后端分离项目中常用的传参方式
    • x-www-form-urlencoded 上传表单数据
    • form-data 上传表单数据
    • xml
    • raw
    • binary

2) 关于请求方式与传参方式

无论是GETPOSTPUT还是DELETE请求方式,从技术上来说,它们几乎都支持上面提到的几种传参方式的——也就是说,即使是【GET发送body参数】,或者【POST发送Query参数】这样奇怪的使用方式,从技术上来说也是支持的!只不过在日常的开发中,我们可能更偏向于【GET发送Query参数】、【POST发送Body参数】这样的搭配使用方式。
关于GET请求能否发送body,可以看这篇文章,讲的非常好:HTTP GET 请求可以有 body 吗?
我这里直接将文章的结论贴出来:

HTTP 协议没有为 GET 请求的 body 赋予语义,也就是既不禁止也不要求 GET 请求带 body
大多数 HTTP 实现从技术上都支持 HTTP GET 请求带 body,少数实现会禁止(google-chrome 浏览器、node-fetch),少数实现会不建议(Fiddler)。

文章中作者的个人建议,也贴出来供参考:

软件工程中有一条原则:不要依赖未定义的行为。HTTP 协议未定义GET请求的 body 语义,如果想用GET请求发送 body,得先为其定义语义,并确保上下游都能很好的支持。作为服务接口的提供方,不应该假设所有的调用方都能发出GET请求 body;作为调用方,不应该假设服务方能完美解析GET请求 body,但如果服务方提供了支持GET请求 body 的接口,可以放心使用,不用纠结。
软件工程中还有另一条原则,不记得原文了,翻译成中国的老话就是:严于律己,宽已待人。我们在写库、写框架、写工具时应该支持GET请求带 body;在封装接口时,尽量不要强制调用方用GET body 提交数据,除非遇到用GET body 才符合逻辑的特殊情况;在使用别人提供的库、框架、工具,或者调用协作方提供的接口时不应该强求对方支持GET请求 body。

下面,将会依次对这几种参数方式进行讲解和代码示例。

3)代码的相关说明

3.1) 项目的pom依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--Hutool是一个小而全的Java工具类库-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.3</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.6</version>
        </dependency>
    </dependencies>

3.2) 代码说明

  1. 下面所有接口的定义,使用的都是@RequestMapping,且都没有指定具体的请求类型——如此故意为之就是为了使得接口从技术上支持各种请求方式(GET POST PUT DELETE等),好方便测试在不同的请求方式下,是否支持各种不同的传参方式。但是大家在平日的业务代码开发中,最好是在接口定义时指定特定的请求方式。
  2. 在下面所有的代码中,我都在方法定义的形参中加上了HttpServletRequest,是因为我要从HttpServletRequest中获取请求的方法类型(request.getMethod())和URI( request.getRequestURI()),从而在接口的返回结果在显现出来,以方便调试。
    大家在自己实际的业务代码中可以根据自身需求决定是否加上这个。不加也不影响入参的接收!
  3. 下面所以接口的定义中,方法返回类型都是string类型,如:
method: [DELETE], uri: [/test_query/requestparam_1], param type: [Query]  ---> SUCCESS! requestParam1 name = wangwu-delete, age = 18

但是看后面调用方调用接口的返回结果,却是如下所示的json串:

{
    "code": 0,
    "data": "method: [DELETE], uri: [/test_query/requestparam_1], param type: [Query]  ---> SUCCESS! requestParam1 name = wangwu-delete, age = 18",
    "msg": "操作成功",
    "timestamp": 1704766444205
}

可以看到方法返回的String内容则是在json串的data这个key当中。
这是因为在我的项目中,我结合@RestControllerAdviceResponseBodyAdvice,对接口返回结果进行了统一的处理。(核心的处理逻辑是:如果接口返回结果类型已经是指定的ResultVO,直接返回;否则将接口返回结果封装到ResultVO对象的data字段中,再返回。)
4. 本文旨在展示在各种请求方式下对不同传参方式的支持情况,因此下面代码中,都没有对接口入参进行任何的校验——即:校验不是本文的重点,关于接口入参的校验,可以去看我的另一篇博客。


1.Query参数

Query参数,表现形式是调用方在调用接口时,入参是拼接在接口的URI后面的,如/test_query/requestparam_1?name=wangwu&age=18

1.1接口定义方式1:在方法形参的位置,把每个参数都平铺开来

  • 定义方式: 在方法形参的位置,把每个参数都平铺开来
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:虽然这种传参方式,从技术上来说可用于GET POST PUT DELETE等请求方式,但是在日常开发中一般偏向于应用在入参个数少(一般少于5个)GET请求方式中
  • 优点:方便简单
  • 缺点:
      1. 调用时入参直接显示在uri中,不太安全
      1. 方法定义时参数个数如果过多,方法体结构会显得很臃肿
      1. 没有入参校验

代码

package com.su.demo.controller.param_type;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;


@RestController
@RequestMapping(value = "/param_type/query")
public class TestQueryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:Query参数
     *     <li><b>定义方式</b>: 在方法形参的位置,把每个参数都平铺开来</li>
     *     <li><b>调用方式</b>: 入参拼接在接口的uri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:虽然这种传参方式,适用于GET POST PUT DELETE等请求方式,但是一般偏向于应用在 入参个数少(一般少于5个)的<b>GET</b>请求方式中</li>
     *     <li><b>优点</b>:方便简单</li>
     *     <li><b>缺点</b>:1) 入参直接显示在uri中,不太安全 2)参数个数如果过多,方法体结构会显得很臃肿; 3)没有入参校验</li>
     * </ul>
     * <p><b>注意</b>:根据自己的需求决定是否在形参中加上HttpServletRequest,我这里是为了从request中获取method,所以加上了</p>
     */
    @RequestMapping(value = "/test1")
    public String test1(String name, Integer age, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age;
    }

}

调用case

注意看上面这个/param_type/query/test1接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以GET 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。

发起请求:
在这里插入图片描述

接口返回结果:
在这里插入图片描述

1.2接口定义方式2:在方法形参的位置,结合@RequestParam把每个参数都平铺开来

  • 定义方式: 在方法形参的位置,结合@RequestParam把每个参数都平铺开来
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:虽然这种传参方式,从技术上来说可用于GET POST PUT DELETE等请求方式,但是在日常开发中一般偏向于应用在入参个数少(一般少于5个)GET请求方式中
  • 优点:方便简单,且结合@RequestParam,可实现入参的必填校验/重命名/默认值等简单的校验功能
  • 缺点:
      1. 调用时入参直接显示在uri中,不太安全
      1. 方法定义时如果参数个数如果过多,方法体结构会显得很臃肿
      1. @RequestParam能支持的校验相对来说还是比较简单

代码

package com.su.demo.controller.param_type;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;

@RestController
@RequestMapping(value = "/param_type/query")
public class TestQueryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:Query参数
     *     <li><b>定义方式</b>: 在方法形参的位置,把每个参数都平铺开来.并且相较于前一种定义方式,这种方式使用 {@link RequestParam}绑定请求参数到方法形参, 且需要注意该注解中的各个属性的作用!</li>
     *     <li><b>调用方式</b>: 入参拼接在接口的uri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:虽然这种传参方式,适用于GET POST PUT DELETE等请求方式,但是一般偏向于应用在 入参个数少(一般少于5个)的<b>GET</b>请求方式中</li>
     *     <li><b>优点</b>:方便简单;且结合{@link RequestParam},可实现入参的必填校验/重命名/默认值等简单的功能</li>
     *     <li><b>缺点</b>:1) 入参直接显示在uri中,不太安全
     *                    2)参数个数如果过多,方法体结构会显得很臃肿(当前这个方法有4个入参,其实就已经有点臃肿了)
     *                    3){@link RequestParam}能支持的校验相对来说还是比较简单</li>
     * </ul>
     */
    @RequestMapping(value = "/test2_requestparam")
    public String test2(@RequestParam String name,
                                @RequestParam(name = "newAge") Integer age,
                                @RequestParam(name = "birth", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birth,
                                @RequestParam(defaultValue = "true") Boolean enable,
                                HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", birth = " + birth + ", enable = " + enable;
    }

}

调用case

注意看上面这个/param_type/query/test2_requestparam接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以GET 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述

可以看到,参数都获取到了。

1.3接口定义方式3:把入参封装到一个实体中

  • 定义方式: 把入参封装到一个实体中
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:虽然这种传参方式,从技术上来说可用于GET POST PUT DELETE等请求方式,但是在日常开发中一般偏向于应用在入参个数多(一般大于5个)GET请求方式中
  • 优点:因为参数都封装在实体对象当中了,所以对参数的个数就没有什么的限制了,接口定义的时候方便多了
  • 缺点:对于这种入参的定义方式来说,它是没有什么缺点的,硬要说缺点的话,其实是针对Query这种传参方式来说的,即:当参数个数一多的时候,参数都放在请求uri中了,一个是不太安全,另外也容易造成uri的长度过过长 (虽然http协议中未明确对url进行长度限制,但在真正实现中,url的长度还是受到限制的,一是服务器端的限制,二就是游览器端的限制)

注:再次申明,在下面代码中,我在方法定义的形参中加上了HttpServletRequest,是因为我要从HttpServletRequest中获取请求的方法类型(request.getMethod())和URI( request.getRequestURI()),从而在接口的返回结果在显现出来,以方便调试。
大家在自己实际的业务代码中可以根据自身需求决定是否加上这个。不加也不影响入参的接收!

代码

package com.su.demo.controller.param_type;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;

@RestController
@RequestMapping(value = "/param_type/query")
public class TestQueryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:Query参数
     *     <li><b>定义方式</b>: 把入参封装到一个实体中(入参个数多于5个时一般用这种方式)
     *     <li><b>调用方式</b>: 入参拼接在接口的uri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说这种传参方式,同时适用于GET POST PUT DELETE等多种请求方式,但是一般我个从偏向于在入参个数大于5个的<b>GET</b>请求方式中使用</li>
     *     <li><b>优点</b>:因为参数都封装在实体bean当中了,所以对参数的个数就没有什么的限制了,接口定义的时候方便多了
     *     <li><b>缺点</b>:对于这种入参的[定义方式]来说,它是没有什么缺点的.硬要说缺点的话,其实是针对Query这种传参方式来说的,即当参数个数一多的时候,参数都放在请求uri中容易造成uri的长度过过长
     *     (虽然http协议中未明确对url进行长度限制,但在真正实现中,url的长度还是受到限制的,一是服务器端的限制,二就是游览器端的限制)</li>
     * </ul>
     */
    @RequestMapping(value = "/test3_entity")
    public String test3(UserDTO userDTO, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO;
    }

}

其中UserDTO 的代码如下:

package com.su.demo.bean.dto;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;

@Data
public class UserDTO {
    /**
     * 主键ID
     */
    private Long id;

    /**
     * 用户姓名
     */
    private String name;

    /**
     * 用户状态 true:启用;false:禁用
     */
    private Boolean enable;

    /**
     * 用户生日
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date birth;
}

调用case

注意看上面这个/param_type/query/test3_entity接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以POST 请求方式的调用结果为例,看是否可以在controller代码中获到调用方传过来的实参。
在这里插入图片描述
可以看到,参数都获取到了。

1.4接口定义方式4:用原生的HttpServletRequest接收参数

  • 定义方式: 在方式形参的位置,用原生的HttpServletRequest接收
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景现在很少用这种方式去接收入参了。
  • 优点:HttpServletRequest是整个请求,可以获取到所有的数据.且HttpServletRequestHttpServletResponse都是内置对象,可以使用
  • 缺点:代码中再去从request中拿到参数,比较麻烦

代码

package com.su.demo.controller.param_type;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;

@RestController
@RequestMapping(value = "/param_type/query")
public class TestQueryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:Query参数
     *     <li><b>定义方式</b>: 在方式形参的位置,用原生的{@link HttpServletRequest}接收
     *     <li><b>调用方式</b>: 入参拼接在接口的uri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下比较少用这种方式来获取参数了</li>
     *     <li><b>优点</b>:{@link HttpServletRequest}是整个请求,可以获取到所有的数据.且HttpServletRequest、HttpServletResponse都是内置对象,可以使用</li>
     *     <li><b>缺点</b>:代码中再去从request中拿到参数,比较麻烦</li>
     * </ul>
     * <p> 注意,这种方式其实也可以获取body请求体里面的数据,参考https://blog.csdn.net/qq_24850045/article/details/121927722
     */
    @RequestMapping(value = "/test4_request")
    public String test4(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        String name = request.getParameter("name");
        Integer age = Integer.parseInt(request.getParameter("age")); // 需要进行类型转换

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = "  + age;
    }

}

调用case

注意看上面这个/param_type/query/test4_request接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以PUT 请求方式的调用结果为例,看是否可以在controller代码中获到调用方传过来的实参。
在这里插入图片描述
可以看到,参数都获取到了。

1.5接口定义方式5:用Map结合RequestParam接收参数

  • 定义方式: 在方式形参的位置,用Map结合RequestParam接收参数
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:适用于参数个数较多,但是自己又想偷懒不想专门定义一个实体来接收入参的场景。一般不推荐使用这种方式
  • 优点:简单方便,偷懒很开心
  • 缺点:1)需要在代码中通过指定特定的key的方式去获取入参,比较麻烦;2)利用map去get到入参之后,还需要手动的去做非空判断+数据类型转换等,很麻烦;3)而且这种接收入参的方式,没有办法结合org.springframework.validation.annotation.Validated来做入参校验!

代码

package com.su.demo.controller.param_type;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;

@RestController
@RequestMapping(value = "/param_type/query")
public class TestQueryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:Query参数
     *     <li><b>定义方式</b>: 在方式形参的位置,结合{@link RequestParam}用Map接收
     *     <li><b>调用方式</b>: 入参拼接在接口的uri后面,如 /test_get/requestparam_1?name=wangwu&age=18</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:适用于参数个数较多,但是自己又想偷懒不想专门定义一个实体来接收入参的场景。一般不推荐使用这种方式。</li>
     *     <li><b>优点</b>:简单方便</li>
     *     <li><b>缺点</b>:需要在代码中通过指定特定的key的方式去获取入参,比较麻烦;而且这种接收入参的方式,没有办法结合{@link org.springframework.validation.annotation.Validated}来做入参校验</li>
     * </ul>
     */
    @RequestMapping(value = "/test5_map")
    public String test5(@RequestParam Map<String, String> paramMap, HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        String name = paramMap.get("name");
        // 判断:如果paramMap.get("age")为null,则age赋默认值0;否则进行入参类型的转换
        Integer age = null == paramMap.get("age") ? 0: Integer.parseInt(paramMap.get("age"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = "  + age;
    }

}

调用case

注意看上面这个/param_type/query/test5_map接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以DELETE 请求方式的调用结果为例,看是否可以在controller代码中获到调用方传过来的实参。
在这里插入图片描述

可以看到,参数都获取到了。


2.Path参数

Path参数,表现形式是入参直接拼接在接口的URI里面,如/test_pathvar/test1/zhangsan/1,其中的zhangsan1就是就是参数。这种方式在REST风格的接口中比较常用。

REST(英文:Representational State Transfer,简称REST,意思:表述性状态转换,描述了一个架构样式的网络系统,比如web应用),一定要记住==它是一种软件架构风格!而不是标准!==它只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
REST指的是一组架构(约束条件)和原则。满足这些(约束条件)和(原则)的应用程序或设计就是 Restful

2.1接口定义方式:用占位符的方式把入参封装到请求路径中

  • 定义方式: 用占位符的方式把入参封装到请求路径中,然后再通过@PathVariable注解可以将URL中的占位符参数绑定到控制器(controller)处理方法的形参中
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等多种请求方式。 但是一般入参个数较少,且你开发的是REST风格的接口,那可以考虑用这种方式
  • 优点和缺点:优点和缺点,其实就是REST风格的接口的优点和缺点了,大家可以自行去查阅

代码

package com.su.demo.controller.param_type;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping(value = "/param_type/path_variable")
public class TestPathVarController {


    /**
     * <ul>
     *     <li><b>入参类别</b>:PathVariable路径参数方式
     *     <li><b>定义方式</b>: 用占位符的方式把入参封装到请求路径中,然后再通过@PathVariable注解可以将URL中的占位符参数绑定到控制器(controller)处理方法的形参中. </li>
     *     <li><b>调用方式</b>: REST风格路径参数,入参拼接在接口的URI里面,如/test_pathvar/test1/zhangsan/1 </li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等多种请求方式.
     *         但是一般入参个数小于5个,且你开发的是REST风格的接口,那可以考虑用这种方式
     *         </li>
     * </ul>
     * <p>REST(英文:Representational State Transfer,简称REST,意思:表述性状态转换,描述了一个架构样式的网络系统,比如web应用),是一种软件架构风格不是标准哦!
     * 只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
     * REST 指的是一组架构(约束条件)和原则。满足这些(约束条件)和(原则)的应用程序或设计就是 Restful。</p>
     */
    @RequestMapping(value = "/test1/{name}/{age}")
    public String test1(@PathVariable("name") String name, @PathVariable("age") Integer myage, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));
        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", myage = "  + myage;
    }
}

调用case

注意看上面这个/param_type/path_variable/test1/{name}/{age}接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以GET 请求方式的调用结果为例,看是否可以在controller代码中获到调用方传过来的实参。
在这里插入图片描述

可以看到,参数都获取到了。


3.Body参数

Body参数,这种方式是把入参放在了请求体当中!它跟前两种入参方式的最大区别,就是:

  1. 前两种入参方式它们的入参都是直接体现在了调用接口时候的URI
  2. 而当前的这种Body参数方式,它的入参是放在了http请求的请求体
    而且,Body参数又可以细分成如下的几种方式:
    • application/json 前后端分离项目中常用的传参方式
    • form-data
    • x-www-form-urlencoded
    • raw
    • binary

下面分别进行说明

3.1 application/json

这种入参方式,入参是json格式的,且放在http请求的请求体当中,并且在发起http请求时Content-Type=application/json

3.1.1 接口定义方式1:在方式形参的位置,用原生的HttpServletRequest接收

  • 定义方式: 在方式形参的位置,用原生的HttpServletRequest接收
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下比较少用这种方式来获取Content-Type=application/json的body参数了
  • 优点:HttpServletRequest是整个请求,可以获取到所有的数据。且HttpServletRequestHttpServletResponse都是内置对象,可以使用
  • 缺点:码中再去从request中拿到参数,老麻烦了,可以看下面的代码
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSON;
import com.su.demo.bean.dto.UserDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/body_json")
public class TestBodyController {


    /**
     * <ul>
     *     <li><b>入参类别</b>:body参数方式
     *     <li><b>定义方式</b>: 在方式形参的位置,用原生的{@link HttpServletRequest}接收
     *     <li><b>调用方式</b>: 参数放在请求体 body 当中
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下比较少用这种方式来获取body参数了</li>
     *     <li><b>优点</b>:{@link HttpServletRequest}是整个请求,可以获取到所有的数据.且HttpServletRequest、HttpServletResponse都是内置对象,可以使用</li>
     *     <li><b>缺点</b>:代码中再去从request中拿到参数,老麻烦,可以看下面的代码</li>
     * </ul>
     * <p> 注意,这种方式其实也可以获取body请求体里面的数据,参考https://blog.csdn.net/qq_24850045/article/details/121927722
     */
    @RequestMapping(value = "/test1_request")
    public String test1(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        ServletInputStream inputStream = request.getInputStream();
        // 用hutool工具包的read方式,将inputStream读取成string
        String body = IoUtil.read(inputStream, "UTF-8");
        System.out.println("body = " + body);
        // 用fastjson将json字符串转换成bean
        UserDTO userDTO = JSON.parseObject(body, UserDTO.class);

        return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO;
    }
}

其中UserDTO的代码如下:

package com.su.demo.bean.dto;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

@Data
public class UserDTO {
    /**
     * 主键ID
     */
    private Long id;

    /**
     * 用户姓名
     */
    private String name;

    /**
     * 用户状态 true:启用;false:禁用
     */
    private Boolean enable;

    /**
     * 用户生日
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birth;
}
调用case

注意看上面这个/param_type/body_json/test1_request接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以POST 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述
可以看到,参数都获取到了。

3.1.2 接口定义方式2:在方式形参的位置,用RequestBody接收

  • 定义方式: 在在方式形参的位置,用RequestBody接收
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下一般用在POSTPUT 请求中
  • 优点:
    • 方法形参的定义非常简洁;
    • 调用的时候参数放在body中,参数体可以很大,不像Query入参方式在当参数过多的时候容易触发服务器端Request header is too large的报错
  • 缺点:用String类型来接收参数,接收之后再转换成bean实体,还是稍微显得有点麻烦
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSON;
import com.su.demo.bean.dto.UserDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/body_json")
public class TestBodyController {

   /**
     * <ul>
     *     <li><b>入参类别</b>:body参数方式
     *     <li><b>定义方式</b>: 在方式形参的位置,用{@link RequestBody}接收
     *     <li><b>调用方式</b>: 参数放在请求体 body 当中
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下一般用在POST或PUT请求中</li>
     *     <li><b>优点</b>:1)方法形参的定义非常简洁;
     *                  2)调用的时候参数放在body中,参数体可以很大,不像Query入参方式在当参数过多的时候容易触发服务器端"Request header is too large"的报错
     *         </li>
     *     <li><b>缺点</b>:用String类型来接收参数,接收之后再转换成bean实体,还是稍微显得有点麻烦</li>
     * </ul>
     */
    @RequestMapping(value = "/test2_requestbody_string")
    public String test2(@RequestBody String body, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        System.out.println("body = " + body);
        // 用fastjson将json字符串转换成bean
        UserDTO userDTO = JSON.parseObject(body, UserDTO.class);

        return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO;
    }

}
调用case

注意看上面这个/param_type/body_json/test2_requestbody_string接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以PUT 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述
可以看到,参数都获取到了。

3.1.3 接口定义方式3:用一个实例来接收RequestBody入参

  • 定义方式: 在方式形参的位置,用RequestBody接收,而且是直接用一个实例类来接收了(spring自动调用相应的org.springframework.http.converter.HttpMessageConverter去做了入参的转换)
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下一般用在POSTPUT 请求中
  • 优点:
    • 方法形参的定义非常简洁;
    • 调用的时候参数放在body中,参数体可以很大,不像Query入参方式在当参数过多的时候容易触发服务器端Request header is too large的报错
  • 缺点:好像没啥缺点

工作中前后端分离项目一般用这用方式比较多

代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSON;
import com.su.demo.bean.dto.UserDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/body_json")
public class TestBodyController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:body参数方式
     *     <li><b>定义方式</b>: 在方式形参的位置,用{@link RequestBody}接收,
     *         而且是直接用一个实例类来接收了(spring自动调用相应的{@link org.springframework.http.converter.HttpMessageConverter}去做转换)
     *         </li>
     *     <li><b>调用方式</b>: 参数放在请求体 body 当中
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下一般用在POST或PUT请求中</li>
     *     <li><b>优点</b>:1)方法形参的定义非常简洁;
     *                  2)调用的时候参数放在body中,参数体可以很大,不像Query入参方式在当参数过多的时候容易触发服务器端"Request header is too large"的报错
     *         </li>
     *     <li><b>缺点</b>:好像没啥缺点</li>
     * </ul>
     */
    @RequestMapping(value = "/test3_requestbody_entity")
    public String test3(@RequestBody UserDTO userDTO, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! userDTO = " + userDTO;
    }

}
调用case

注意看上面这个/param_type/body_json/test3_requestbody_entity接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以DELETE 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述

可以看到,参数都获取到了。

3.1.4 接口定义方式4:用Map来接收RequestBody入参

  • 定义方式: 在方式形参的位置,用RequestBody接收,而且是直接用一个Map的实例类来接收了
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:适用于参数个数较多,但是自己又想偷懒不想专门定义一个实体来接收入参的场景。一般不推荐使用这种方式
  • 优点:简单方便,偷懒很开心
  • 缺点:1)需要在代码中通过指定特定的key的方式去获取入参,比较麻烦;2)利用map去get到入参之后,还需要手动的去做非空判断+数据类型转换等,很麻烦;3)而且这种接收入参的方式,没有办法结合org.springframework.validation.annotation.Validated来做入参校验!

一般不推荐使用这种方式

代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSON;
import com.su.demo.bean.dto.UserDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/body_json")
public class TestBodyController {

   /**
     * <ul>
     *     <li><b>入参类别</b>:body参数方式
     *     <li><b>定义方式</b>: 在方式形参的位置,用{@link RequestBody}接收,
     *         而且是直接用一个Map对象实例类来接收了(spring自动调用相应的{@link org.springframework.http.converter.HttpMessageConverter}去做转换)
     *         </li>
     *     <li><b>调用方式</b>: 参数放在请求体 body 当中
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,一般使用在偷懒不想专门定义一个实体来接收入参的场景。一不太推荐使用这种方式/li>
     *     <li><b>优点</b>:1)方法形参的定义非常简洁;
     *                  2)调用的时候参数放在body中,参数体可以很大,不像Query入参方式在当参数过多的时候容易触发服务器端"Request header is too large"的报错
     *         </li>
     *     <li><b>缺点</b>:好像没啥缺点</li>
     * </ul>
     */
    @RequestMapping(value = "/test4_requestbody_map")
    public String test4(@RequestBody Map<String, String> paramMap, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        String name = paramMap.get("name");
        // 判断:如果paramMap.get("age")为null,则age赋默认值0;否则进行入参类型的转换
        Integer age = null == paramMap.get("age") ? 0: Integer.parseInt(paramMap.get("age"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age;
    }

}
调用case

注意看上面这个/param_type/body_json/test4_requestbody_map接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证),为了节约文章篇幅,下面只以POST 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述

可以看到,参数都获取到了。

3.2 x-www-form-urlencoded

x-www-form-urlencodedform-data,两者都可以用来上传前端表单数据,下面简单将两者的不同列举出来,更详细的可自行上网查询或者看下面博客 form-data、x-www-form-urlencoded的区别或者form-data与x-www-form-urlencoded的区别以及知识延伸
1.支持的入参类型不同x-www-form-urlencoded只支持普通的文本内容,不支持上传File文件!而form-data则支持上传File文件(如图片、音频、视频)。
2. 编码不同x-www-form-urlencoded的编码方式就隐藏在名字里urlencoded,即使用js中encodeURI()函数;而form-data的格式,要比 x-www-form-urlencoded复杂的多,它会把内容分成多个部分,每个部分都支持不同的格式
3. x-www-form-urlencoded占用字节少,form-data占用字节多

x-www-form-urlencoded会将表单内的数据转换为键值对,比如name=lisi&age=23,并放在请求体body中进行传输

3.2.1 接口定义方式1:在方法形参的位置,把每个参数都平铺开来

  • 定义方式: 方法形参的位置,把每个参数都平铺开来,也可以使用@RequestParam注解
  • 兼容的请求方式POST PUT DELETE注意,不兼容GET请示方式
  • 适用场景:虽然这种传参方式,适用于POST PUT DELETE等请求方式,但是一般偏向于应用在 用POSTPUT方式去发送的表单数据,且参数个数少,且表单字段都是普通类型(即表单字段不是File文件)
  • 优点:方便简单
  • 缺点:1)参数个数如果过多,方法体结构会显得很臃肿
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form_urlencoded")
public class TestFormUrlEncodeController {

    /**
     * <p>注意,如果使用GET请求方式,则后端获取不到入参!!!</p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 在方法形参的位置,把每个参数都平铺开来</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     *     <li><b>兼容的请求方式</b>:POST PUT DELETE</li>
     *     <li><b>适用场景</b>:虽然这种传参方式,适用于POST PUT DELETE等请求方式,但是一般偏向于应用在 用<b>POST</b>或b>PUT</b>方式去发送的表单数据,且参数个数少,且表单字段都是普通类型(即表单字段不是File文件),</li>
     *     <li><b>优点</b>:方便简单</li>
     *     <li><b>缺点</b>:1)参数个数如果过多,方法体结构会显得很臃肿</li>
     * </ul>
     */
    @RequestMapping(value = "/test1_requestparam")
    public String test1(String name, @RequestParam(name = "age", required = false) Integer age, Boolean enable, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", enable = " + enable;
    }
}
调用case

注意看上面这个/param_type/body_form_urlencoded/test1_requestparam接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)——经过实测发现,当请示方式为GET 的时候,后端代码获取不到入参!而当请求方式为POST PUT DELETE的时候,后端可以获取到入参。
下面是示例

GET,传参失败

在这里插入图片描述

可以看到,没有获取到入参!

POST PUT DELETE 传参成功

为了节约文章篇幅,下面只以POST 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述

可以看到,参数都获取到了。

3.2.2 接口定义方式2:用一个实例来接收入参

  • 定义方式: 把入参封装到一个实体中,且实体一定不能使用@RequestParam@RequestBody注解修饰
  • 兼容的请求方式POST PUT DELETE注意,不兼容GET请示方式
  • 适用场景:虽然这种传参方式,适用于POST PUT DELETE等请求方式,但是一般偏向于应用在 用POSTPUT方式去发送的表单数据,且表单字段都是普通类型(即表单字段不是File文件)
  • 优点:方便简单,不管表单字段个个数多或少,一般如果表单字段类型没有File文件类型的,都可以使用这种方式
  • 缺点:缺点的话就是针对Content-Type=application/x-www-form-urlencoded这种入参方式来说了:不能上传文件
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form_urlencoded")
public class TestFormUrlEncodeController {

    /**
     * <p>注意,如果使用GET请求方式,则后端获取不到入参!!!只支持POST PUT DELETE</p>
     * <p>且形参定义那里,一定不能使用{@link RequestParam}修饰</p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 把入参封装到一个实体中(入参个数多于5个时一般用这种方式)</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     *     <li><b>兼容的请求方式</b>:POST PUT DELETE</li>
     *     <li><b>适用场景</b>:虽然这种传参方式,适用于POST PUT DELETE等请求方式,但是一般偏向于应用在 用<b>POST</b>或<b>PUT</b>方式去发送的表单数据,且表单字段都是普通类型(即表单字段不是File文件),</li>
     *     <li><b>优点</b>:方便简单,不管表单字段个个数多或少,一般如果表单字段类型没有File文件类型的,都可以使用这种方式</li>
     *     <li><b>缺点</b>:缺点的话就是针对Content-Type=application/x-www-form-urlencoded这种入参方式来说了:不能上传文件</li>
     * </ul>
     */
    @RequestMapping(value = "/test2_entity")
    public String test2(TeacherDTO teacherDTO, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! teacherDTO = " + teacherDTO;
    }

}
调用case

注意看上面这个/param_type/body_form_urlencoded/test2_entity接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)——经过实测发现,当请示方式为GET 的时候,后端代码获取不到入参!而当请求方式为POST PUT DELETE的时候,后端可以获取到入参。
下面是示例

GET,传参失败

在这里插入图片描述
可以看到,没有获取到入参!

POST PUT DELETE 传参成功

为了节约文章篇幅,下面只以PUT 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述
可以看到,参数都获取到了。

3.2.3 接口定义方式3:用原生的HttpServletRequest接收参数

  • 定义方式: 用原生的HttpServletRequest接收参数
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:一般很少这种方式来接收Content-Type=application/x-www-form-urlencoded的请求
  • 优点:获取入参很麻烦,没啥优点
  • 缺点:获取入参很麻烦

注意:

  1. 如果是GET请求方式,则:通过request.getInputStream()方式是可以获取入参的,获取到的数据示例:name=wangwu&age=12 ,但是通过request.getParameter("xxx")方式,却获取不到入参!
  2. 如果是 POST PUT DELETE请求方式,则正好相反: 通过request.getInputStream()方式无法获取入参,通过request.getParameter("xxx")方式则可以获取入参

下面代码是以request.getParameter("xxx")方式去获取入参

代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form_urlencoded")
public class TestFormUrlEncodeController {

    /**
     * <p>如果是GET请求方式,则:通过request.getInputStream()方式可以获取入参,获取到的数据示例:name=wangwu&age=12  但是通过request.getParameter("xxx")方式,却获取不到入参!</p>
     * <p>如果是POST PUT DELETE请求方式,则正好相反: 通过request.getInputStream()方式无法获取入参,通过request.getParameter("xxx")方式则可以获取入参</p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 在方法形参的位置,用{@link HttpServletRequest}来接收入参</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     *     <li><b>兼容的请求方式</b>:POST PUT DELETE</li>
     *     <li><b>适用场景</b>:一般很少这种方式来接收Content-Type=application/x-www-form-urlencoded的请求</li>
     *     <li><b>缺点</b>:获取入参很麻烦</li>
     * </ul>
     */
    @RequestMapping(value = "/test3_request")
    public String test3(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        String name = request.getParameter("name");
        Integer age = null == request.getParameter("age") ? 0 : Integer.parseInt(request.getParameter("age"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age;
    }

}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式——但是经过实测发现,当通过request.getParameter("xxx")方式去获取入参时,则只有请示方式是POST PUT DELETE的时候,后端代码才能获取到入参!而当请求方式为GET 的时候,后端获取入参失败。
下面是示例

GET,传参失败

在这里插入图片描述
获取不到入参!

POST PUT DELETE 传参成功

为了节约文章篇幅,下面只以POST 请求方式的调用结果为例,看是否可以在controller代码中获取到调用方传过来的实参。
在这里插入图片描述
可以看到,可以获取到参数!

3.2.4 接口定义方式4:用Map接收参数

  • 定义方式: 在方法形参的位置,用Map来接收入参,并且,map前面一定要加上RequestParam注解,否则map也拿不到入参!
  • 兼容的请求方式POST PUT DELETE注意,不兼容GET请示方式
  • 适用场景:由于当请求的Content-Type=application/x-www-form-urlencoded时,只支持POST PUT DELETE请求方式, 但是这种方式对于入参的获取校验处理也很麻烦,所以一般不用这种方式来接收Content-Type=application/x-www-form-urlencoded的请求
  • 优点:麻烦,没啥优点
  • 缺点:获取并处理校验入参很麻烦
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form_urlencoded")
public class TestFormUrlEncodeController {

    /**
     * <p>不支持GET请求类型</p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 在方法形参的位置,用Map来接收入参,并且,map前面一定要加上{@link RequestParam}注解,否则map也拿不到入参!</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     *     <li><b>兼容的请求方式</b>:POST PUT DELETE</li>
     *     <li><b>适用场景</b>:由于当请求的Content-Type=application/x-www-form-urlencoded时,只支持POST PUT DELETE请求方式,
     *     但是这种方式对于入参的获取校验处理也很麻烦,所以一般不用这种方式来接收Content-Type=application/x-www-form-urlencoded的请求</li>
     *     <li><b>优点</b>:麻烦,没啥优点</li>
     *     <li><b>缺点</b>:获取并处理校验入参很麻烦</li>
     * </ul>
     */
    @RequestMapping(value = "/test4_map")
    public String test4(@RequestParam Map<String, String> paramMap, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        String name = paramMap.get("name");
        // 判断:如果paramMap.get("age")为null,则age赋默认值0;否则进行入参类型的转换
        Integer age = null == paramMap.get("age") ? 0: Integer.parseInt(paramMap.get("age"));

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age;
    }

}
调用case

请求方式GET无法将参数传给后端,下面以直接以PUT请求方式作为示例
在这里插入图片描述
可以看到,可以获取到入参

3.2.5 (error)接口定义方式5:每个参数都平铺开来,并尝试获取File文件入参

先说结论,虽然在下面方法定义的,=形参中添加了MultipartFile,但是如果调用接口的时候入参类型是Content-Type=application/x-www-form-urlencoded, 那由于Content-Type=application/x-www-form-urlencoded入参类型限制了入参就无法将【文件】传过来,所以该方法的MultipartFile对象始终为空!

  • 定义方式: 方法形参的位置,把每个参数都平铺开来,也可以使用@RequestParam注解,是时尝试使用MultipartFile类型入参,去接收文件
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form_urlencoded")
public class TestFormUrlEncodeController {

    /**
     * <p>
     *     虽然在方法定义这里,形参中添加了MultipartFile,但是如果调用接口的时候入参类型是Content-Type=application/x-www-form-urlencoded,
     *     那由于Content-Type=application/x-www-form-urlencoded入参类型限制了入参就无法将【文件】传过来,所以该方法的MultipartFile对象始终为空!
     * </p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 在方法形参的位置,把每个参数都平铺开来</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     * </ul>
     */
    @RequestMapping(value = "/test5_requestparam_file")
    public String test5(String name, Integer age, Boolean enable, MultipartFile myfile, HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));
        String fileName = null;
        if (null != myfile) {
            fileName = myfile.getOriginalFilename();
        }

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", enable = " + enable + ", fileName = " + fileName;
    }

}
调用case

实测发现,不管是哪种请求方式,只要请求的时候Content-Type=application/x-www-form-urlencoded,那都无法将文件传给后端!
下面是用POST请求方式作示例:
在这里插入图片描述

3.3 form-data

x-www-form-urlencodedform-data它们两个的区别在上面已经说过了,这里不再赘述。
其实form-data这种入参方式,对应的接口的定义方式,跟x-www-form-urlencoded几乎是一样的。

3.3.1 接口定义方式1:在方法形参的位置,把每个参数都平铺开来

  • 定义方式: 方法形参的位置,把每个参数都平铺开来,也可以使用@RequestParam注解
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:虽然这种传参方式,适用于POST PUT DELETE等请求方式,但是一般偏向于应用在 用POSTPUT方式去发送的表单数据,且参数个数少的情形
  • 优点:方便简单
  • 缺点:参数个数如果过多,方法体结构会显得很臃肿,获取文件类型入参也很麻烦
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form")
public class TestFormUrlEncodeController {
    /**
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=multipart/form-data
     *     <li><b>定义方式</b>: 在方法形参的位置,把每个参数都平铺开来(文件类型也平铺开来)</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=multipart/form-data</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:虽然这种传参方式,适用于GET POST PUT DELETE等请求方式,但是一般偏向于应用在 用<b>POST</b>或b>PUT</b>方式去发送的表单数据,且参数个数少</li>
     *     <li><b>优点</b>:方便简单</li>
     *     <li><b>缺点</b>:1)参数个数如果过多,方法体结构会显得很臃肿,获取文件类型入参也很麻烦</li>
     * </ul>
     */
    @RequestMapping(value = "/test1_requestparam")
    public String test1(String name,
                        @RequestParam(name = "age", required = false) Integer age,
                        Boolean enable,
                        MultipartFile myfile,
                        HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        StringBuilder sb = new StringBuilder();
        // 文件处理(如果上传的文件不为空,则把文件的第一行读取出来,放在接口返回结果中)
        if (null != myfile) {
            String fileName = myfile.getOriginalFilename();
            sb.append(" fileName = ").append(fileName);

            ArrayList<String> dataList = IoUtil.readLines(myfile.getInputStream(), "UTF8", new ArrayList<String>());
            Optional<String> firstLineOpt = dataList.stream().findFirst();
            if (firstLineOpt.isPresent()) {
                sb.append(", firstLine = " + firstLineOpt.get());
            }
        }

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", enable = " + enable + ", ===> 【fileDetail】: " + sb.toString();
    }
}
调用case

注意看上面这个/param_type/body_form_urlencoded/test1_requestparam接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以POST请求作为示例
在这里插入图片描述
可以看到,参数都获取到了。

3.3.2 接口定义方式2:把入参封装到一个实体中

  • 定义方式: 把入参封装到一个实体中,并且形参定义那里,一定不能使用RequestParamRequestBody修饰,否则调用方无法传参
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:虽然这种传参方式,适用于GET POST PUT DELETE等请求方式,但是一般偏向于应用在 用POST PUT方式去发送的表单数据,且表单字段有可能有File文件
  • 优点:方便简单,不管表单字段个数多或少,都可以使用这种方式,一般也是推荐使用这种方式去接收form-data表单数据
  • 缺点:没啥缺点
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form")
public class TestFormUrlEncodeController {

    /**
     * <p>形参定义那里,一定不能使用{@link RequestParam}或{@link RequestBody}修饰,否则调用方无法传参</p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=multipart/form-data
     *     <li><b>定义方式</b>: 把入参封装到一个实体中(入参个数多于5个时一般用这种方式)</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=multipart/form-data</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:虽然这种传参方式,适用于GET POST PUT DELETE等请求方式,但是一般偏向于应用在 用<b>POST</b>或<b>PUT</b>方式去发送的表单数据,且表单字段有可能有File文件</li>
     *     <li><b>优点</b>:方便简单,不管表单字段个个数多或少,都可以使用这种方式</li>
     *     <li><b>缺点</b>:没啥缺点</li>
     * </ul>
     */
    @RequestMapping(value = "/test2_entity")
    public String test2(StudentDTO studentDTO, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! studentDTO = " + studentDTO;
    }

}

其中StudentDTO 类的代码如下:

注意下面代码中重写了toString方法,方便把文件名称给输出出来

package com.su.demo.bean.dto;

import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;

@Data
public class StudentDTO {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 附件
     */
    private MultipartFile[] myfile;

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (null != myfile) {
            Arrays.stream(myfile)
                    .forEach(multipartFile -> sb.append(", fileName = ").append(multipartFile.getOriginalFilename()));
        }
        return "StudentDTO{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", 【fileDetail】: " + sb.toString() +
                '}';
    }
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以PUT请求作为示例
在这里插入图片描述

可以看到,参数都获取到了。

3.3.3 接口定义方式3:在方法形参的位置,用HttpServletRequest来接收入参

  • 定义方式: 在方法形参的位置,用HttpServletRequest来接收入参
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:从技术上来说,它支持GET POST、PUT、DELETE等请求方式发送的Content-Type=multipart/data-form的http请求。但是这种方式获取参数真的很麻烦,不太推荐使用
  • 优点:没啥优点
  • 缺点:需要自己在代码中显式的从request中去取数据(尤其文件),很麻烦
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form")
public class TestFormUrlEncodeController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=multipart/data-form
     *     <li><b>定义方式</b>: 在方法形参的位置,用{@link HttpServletRequest}来接收入参</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=multipart/data-form</li>
     *     <li><b>兼容的请求方式</b>:GET POST、PUT、DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,它支持GET POST、PUT、DELETE等请求方式发送的Content-Type=multipart/data-form的http请求。但是这种方式获取参数真的很麻烦,不太推荐使用</li>
     *     <li><b>优点</b>:没啥优点</li>
     *     <li><b>缺点</b>:需要自己在代码中显式的从request中去取数据(尤其文件),很麻烦</li>
     * </ul>
     */
    @RequestMapping(value = "/test3_request")
    public String test3(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));
        // [注]:直接从request.getInputStream()中是无法获取数据的,只能通过request.getParameter("xxx")的方式去获取
        
        String name = request.getParameter("name");
        Integer age = null == request.getParameter("age") ? 0 : Integer.parseInt(request.getParameter("age"));
        // 获取文件
        StringBuilder sb = new StringBuilder();
        MultiValueMap<String, MultipartFile> multiFileMap = ((StandardMultipartHttpServletRequest) request).getMultiFileMap();
        if (!CollectionUtils.isEmpty(multiFileMap)) {
            List<MultipartFile> multipartFileList = multiFileMap.get("myfile");
            if (!CollectionUtils.isEmpty(multipartFileList)) {
                for (MultipartFile multipartFile : multipartFileList) {
                    sb.append(" , fileName = ").append(multipartFile.getOriginalFilename());
                }
            }
        }

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", 【fileDetail】: " + sb.toString();
    }

}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以GET请求作为示例
在这里插入图片描述
可以看到,参数都获取到了。

3.3.4 接口定义方式4:用Map来接收入参

  • 定义方式: 在方法形参的位置,用Map来接收入参,并且,map前面一定要加上RequestParam注解,否则map也拿不到入参!
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:从技术上来说,它支持GET POST PUT DELETE等请求方式发送的Content-Type=multipart/data-form的http请求。但是这种方式获取参数真的很麻烦,不太推荐使用
  • 优点:没啥优点
  • 缺点:需要自己在代码中显式的从request中去取数据(尤其文件),很麻烦
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.TeacherDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

@RestController
@RequestMapping(value = "/param_type/body_form")
public class TestFormUrlEncodeController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 在方法形参的位置,用Map来接收入参,并且,map前面一定要加上{@link RequestParam}注解,否则map也拿不到入参!</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     *     <li><b>兼容的请求方式</b>:GET POST、PUT、DELETE</li>
     *     <li><b>适用场景</b>:从技术上来说,它支持GET POST、PUT、DELETE等请求方式发送的Content-Type=multipart/data-form的http请求。但是这种方式获取参数真的很麻烦,不太推荐使用</li>
     *     <li><b>优点</b>:没啥优点</li>
     *     <li><b>缺点</b>:需要自己在代码中显式的从request中去取数据(尤其文件),很麻烦</li>
     * </ul>
     */
    @RequestMapping(value = "/test4_map")
    public String test4(@RequestParam Map<String, Object> paramMap, HttpServletRequest request) {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        String name = (String) paramMap.get("name");
        // 判断:如果paramMap.get("age")为null,则age赋默认值0;否则进行入参类型的转换
        Integer age = null == paramMap.get("age") ? 0: Integer.parseInt((String) paramMap.get("age"));

        // map中是没有办法获取文件的,只能再从request中去获取(可以看到,获取的代码写的很麻烦)
        StringBuilder sb = new StringBuilder();
        MultiValueMap<String, MultipartFile> multiFileMap = ((StandardMultipartHttpServletRequest) request).getMultiFileMap();
        if (!CollectionUtils.isEmpty(multiFileMap)) {
            List<MultipartFile> multipartFileList = multiFileMap.get("myfile");
            if (!CollectionUtils.isEmpty(multipartFileList)) {
                for (MultipartFile multipartFile : multipartFileList) {
                    sb.append(" , fileName = ").append(multipartFile.getOriginalFilename());
                }
            }
        }

        return requestMetaInfo + " ---> SUCCESS! name = " + name + ", age = " + age + ", 【fileDetail】: " + sb.toString();
    }

}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以DELETE请求作为示例
在这里插入图片描述
可以看到,参数都获取到了。

3.4 xml

这种入参方式,入参是xml格式的,且放在http请求的请求体当中,并且在发起http请求时Content-Type=application/xml。 xml格式内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<bookDTO>
    <name>三国演义</name>
    <price>66</price>
</bookDTO>

由于Content-Type=application/xml这种传参方式我平日没有使用过,所以这里示例用的xml格式比较简单

3.4.1 接口定义方式1:用一个实体接收入参

  • 定义方式: 用一个实体接收入参,且注意实体使用了@XmlRootElement注解进行标注
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:需要接收xml类型内容的场景
  • 优点:方便简单, xml内容直接映射到实体里面
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.BookDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/xml")
public class TestXmlController {


    /**
     * <ul>
     *     <li><b>入参类别</b>:xml格式的内容,放在body中进行传参
     *     <li><b>定义方式</b>: 用一个实体接收入参</li>
     *     <li><b>调用方式</b>: xml内容放在请求的body中,且Content-Type=application/xml</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:需要接收xml类型内容的场景</li>
     *     <li><b>优点</b>:方便简单, xml内容直接映射到实体里面</li>
     *     <li><b>缺点</b>:</li>
     * </ul>
     */
    @RequestMapping(value = "/test1_entity")
    public String test1(@RequestBody BookDTO bookDTO, HttpServletRequest request){
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS! userDTO = " + bookDTO;
    }
}

其中,BookDTO的代码如下所示,注意它使用了@XmlRootElement注解

package com.su.demo.bean.dto;

import lombok.Data;
import javax.xml.bind.annotation.XmlRootElement;

@Data
@XmlRootElement
public class BookDTO {
    private String name;
    private Double price;
}

调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以POST请求作为示例
在这里插入图片描述
可以看到,参数都获取到了。

3.4.2 接口定义方式2:用原生的HttpServletRequest接收

  • 定义方式: 在方式形参的位置,用原生的HttpServletRequest接收
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:需要接收xml类型内容的场景
  • 优点:直接通过request.getInputStream()的方式,可以以String形式获取到全部的xml的内容,之后代码在爱咋解析就咋解析。
  • 缺点:后续的解析也是个麻烦……
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.BookDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/xml")
public class TestXmlController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:xml格式的内容,放在body中进行传参数
     *     <li><b>定义方式</b>: 在方式形参的位置,用原生的{@link HttpServletRequest}接收
     *     <li><b>调用方式</b>: 参数放在请求体 body 当中
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:需要接收xml类型内容的场景</li>
     *     <li><b>优点</b>:{@link HttpServletRequest}是整个请求,可以获取到所有的数据.且HttpServletRequest、HttpServletResponse都是内置对象,可以使用</li>
     *     <li><b>缺点</b>:代码中再去从request中拿到参数,老麻烦,可以看下面的代码</li>
     * </ul>
     */
    @RequestMapping(value = "/test2_request")
    public String test2(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        ServletInputStream inputStream = request.getInputStream();
        // 用hutool工具包的read方式,将inputStream读取成string
        String body = IoUtil.read(inputStream, "UTF-8");
        System.out.println("body = " + body);
        // 拿到body之后,可以再自行进行xml格式的解析,并从中获取数据

        return requestMetaInfo + " ---> SUCCESS!  input body = " + body;
    }
    
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以POST请求作为示例
在这里插入图片描述

可以看到,参数都获取到了。

(error)3.4.3 接口定义方式3:用原生的HttpServletRequest接收

提前说明,这种定义方式,是无法获取到Content-Type=application/xml的body参数的!

  • 定义方式: 在方法形参的位置,把每个参数都平铺开来
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import com.su.demo.bean.dto.BookDTO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@RestController
@RequestMapping(value = "/param_type/xml")
public class TestXmlController {

    /**
     * <p>这种接口定义方式,接收不到Content-Type=application/xml的body参数</p>
     * <ul>
     *     <li><b>入参类别</b>:xml格式的内容,放在body中进行传参数
     *     <li><b>定义方式</b>: 在方法形参的位置,把每个参数都平铺开来
     *     <li><b>调用方式</b>: xml格式的内容,放在请求体 body 当中,且Content-Type=application/xml
     * </ul>
     */
    @RequestMapping(value = "/test3_query")
    public String test3(String name, @RequestParam(value = "price", required = false) Double price, HttpServletRequest request){
        return "name = " + name + ", price = " + price;
    }
    
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式。但是实测发现,无论使用哪种请求方式,后端都获取不到入参!下面只以POST请求作为示例
在这里插入图片描述
可以看到,参数获取不到。

3.5 raw

raw原始类型,可以上传任意格式的文本,比如 text、json、xml、html等。

3.5.1 接口定义方式1:用@RequestBody接收

  • 定义方式: 用@RequestBody和String类型入参接收参数
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:任意格式的【文本】内容,放在请求体 body 当中,且Content-Type=text/plain
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping(value = "/param_type/raw")
public class TestRawController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:任意格式的【文本】,比如 text、json、xml、html等,x放在body中进行传参数
     *     <li><b>定义方式</b>: 用{@link RequestBody}和String类型入参接收参数</li>
     *     <li><b>调用方式</b>: 任意格式的【文本】内容,放在请求体 body 当中,且Content-Type=text/plain
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:任意格式的【文本】内容,放在请求体 body 当中,且Content-Type=text/plain</li>
     * </ul>
     */
    @RequestMapping("/test1_requestbody")
    public String test1(@RequestBody String body, HttpServletRequest request){
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS!  input body = " + body;
    }
    
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以POST请求作为示例
在这里插入图片描述

可以看到,参数获取到了。

3.5.2 接口定义方式2:用原生的HttpServletRequest接收

  • 定义方式: 用原生的HttpServletRequest接收参数
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:任意格式的【文本】内容,放在请求体 body 当中,且Content-Type=text/plain
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping(value = "/param_type/raw")
public class TestRawController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:任意格式的【文本】,比如 text、json、xml、html等,x放在body中进行传参数
     *     <li><b>定义方式</b>: 用原生的{@link HttpServletRequest}接收参数</li>
     *     <li><b>调用方式</b>: 任意格式的【文本】内容,放在请求体 body 当中,且Content-Type=text/plain
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:任意格式的【文本】</li>
     * </ul>
     */
    @RequestMapping("/test2_request")
    public String test2(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        ServletInputStream inputStream = request.getInputStream();
        // 用hutool工具包的read方式,将inputStream读取成string
        String body = IoUtil.read(inputStream, StandardCharsets.UTF_8);
        System.out.println("body = " + body);
        // 拿到body之后,可以再自行进行xml格式的解析,并从中获取数据

        return requestMetaInfo + " ---> SUCCESS!  input body = " + body;
    }
    
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以PUT请求作为示例
在这里插入图片描述
可以看到,参数获取到了。

3.6 binary

binary,对应着http请求中的Content-Type:application/octet-stream,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件

3.6.1 接口定义方式1:用@RequestBody接收

  • 定义方式: 用@RequestBody和String类型入参接收参数
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:http请求中的Content-Type:application/octet-stream,上传二进制数据
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping(value = "/param_type/binary")
public class TestBinaryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:对应着http请求中的Content-Type:application/octet-stream,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件</li>
     *     <li><b>定义方式</b>: 用{@link RequestBody}和String类型入参接收参数</li>
     *     <li><b>调用方式</b>: http请求中的Content-Type:application/octet-stream,上传二进制数据</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:http请求中的Content-Type:application/octet-stream,上传二进制数据</li>
     * </ul>
     */
    @RequestMapping("/test1_requestbody")
    public String test1(@RequestBody String body, HttpServletRequest request){
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        return requestMetaInfo + " ---> SUCCESS!  input body = " + body;
    }
    
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以POST请求作为示例
在这里插入图片描述
可以看到,参数获取到了。

3.6.2 接口定义方式2:用原生的HttpServletRequest接收

  • 定义方式: 用原生的HttpServletRequest接收参数
  • 兼容的请求方式GET POST PUT DELETE
  • 适用场景:http请求中的Content-Type:application/octet-stream,上传二进制数据
代码
package com.su.demo.controller.param_type;

import cn.hutool.core.io.IoUtil;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping(value = "/param_type/binary")
public class TestBinaryController {

    /**
     * <ul>
     *     <li><b>入参类别</b>:对应着http请求中的Content-Type:application/octet-stream,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件</li>
     *     <li><b>定义方式</b>: 用{@link RequestBody}和String类型入参接收参数</li>
     *     <li><b>调用方式</b>: http请求中的Content-Type:application/octet-stream,上传二进制数据</li>
     *     <li><b>兼容的请求方式</b>:GET POST PUT DELETE</li>
     *     <li><b>适用场景</b>:http请求中的Content-Type:application/octet-stream,上传二进制数据</li>
     * </ul>
     */
    @RequestMapping("/test2_request")
    public String test2(HttpServletRequest request) throws IOException {
        // 从request中获取一些接口请求时的元数据信息,包括请求方式,Content-type等
        String requestMetaInfo = String.format("method: [%s], uri: [%s], Content-Type: [%s] ", request.getMethod(), request.getRequestURI(),  request.getHeader("Content-Type"));

        ServletInputStream inputStream = request.getInputStream();
        // 用hutool工具包的read方式,将inputStream读取成string
        String body = IoUtil.read(inputStream, StandardCharsets.UTF_8);
        System.out.println("body = " + body);
        // 拿到body之后,可以再自行进行xml格式的解析,并从中获取数据

        return requestMetaInfo + " ---> SUCCESS!  input body = " + body;
    }
    
}
调用case

注意看上面这个接口的定义方式,它并没有指定请求方式,因此它支持GET POST PUT DELETE等所有的请求方式(已经过验证)。为节约篇幅,下面只以POST请求作为示例
在这里插入图片描述

可以看到,参数获取到了。

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值