java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot接收接口入参

SpringBoot接收接口入参的方式小结

作者:象牙酥

这篇文章主要给大家介绍了SpringBoot接收接口入参的几种方式,我们从调用方的视角去看待这个问题,对调用方来说,它在调用接口时有好几种传参方式,下面,将会依次对这几种参数方式进行讲解和代码示例,需要的朋友可以参考下

引言

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

Query参数。表现形式是调用方在调用接口时,入参是拼接在接口的URI后面的,如/test_get/requestparam_1?name=wangwu&age=18。这种方式在入参个数比较少GET请求方式中比较常用。

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

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

1)前两种入参方式它们的入参都是直接体现在了调用接口时候的URI

2)而当前的这种Body参数方式,它的入参是放在了Body请求体

而且,Body参数又可以细分成如下的几种方式:

application/json 前后端分离项目中常用的传参方式

x-www-form-urlencoded 上传表单数据

form-data 上传表单数据

raw

binary

另外需要强调的是,无论是GET、POST、PUT还是DELETE请求方式,从技术上来说,它们是都支持上面提到的几种传参方式的!只不过在日常的开发中,我们可能习惯了GET+Query参数或者POST+Body参数(application/json)这样的搭配使用方式

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

重要说明

项目的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>

代码的说明

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:在方法形参的位置,把每个参数都平铺开来

代码

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把每个参数都平铺开来

代码

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:把入参封装到一个实体中

注:再次申明,在下面代码中,我在方法定义的形参中加上了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接收参数

代码

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接收参数

代码

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接口定义方式:用占位符的方式把入参封装到请求路径中

代码

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

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

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

代码

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接收

代码

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入参

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

代码

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入参

一般不推荐使用这种方式

代码

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,两者都可以用来上传前端表单数据,下面简单将两者的不同列举出来

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:在方法形参的位置,把每个参数都平铺开来

代码

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:用一个实例来接收入参

代码

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接收参数

代码

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>
     * <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>:GET</li>
     *     <li><b>适用场景</b>:由于当请求的Content-Type=application/x-www-form-urlencoded时,只支持GET请求方式,
     *     并且即使是GET请求方式能获取到入参了,之后的校验和处理也很麻烦,所以一般不用这种方式来接收Content-Type=application/x-www-form-urlencoded的请求</li>
     *     <li><b>优点</b>:方便简单,且这接收入参的方式,直接调用方用GET的请求方式传参</li>
     *     <li><b>缺点</b>:1)需要自己在代码中显式的从request的inputStream流【获取+转换+解码】数据,很麻烦
     *                    2)如果请求方式是POST PUT DELETE等,同时Content-Type=application/x-www-form-urlencoded,则无法将入参传到后面代码中!
     *      </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"));

        ServletInputStream inputStream = request.getInputStream();
        String inputStr = IoUtil.read(inputStream, "UTF-8");
        String decodeRes = URLDecoder.decode(inputStr, "UTF-8");

        return requestMetaInfo + " ---> SUCCESS! " + inputStr + ", decodeResult = " + decodeRes;
    }

}

调用case

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

GET,传参成功

在这里插入图片描述

可以看到,获取到入参!

POST PUT DELETE 传参失败

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

在这里插入图片描述

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

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

提前说明:这是一种错误的定义方式,这种方式,无论是否在map前面添加RequestParam还是RequestBody, 后端代码都获取不到 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>这是一种错误的定义方式,这种方式,无论是否在map前面添加{@link RequestParam}还是{@link RequestBody}, 代码都获取不到 Content-Type=application/x-www-form-urlencoded类型请求的入参!!!</p>
     * <ul>
     *     <li><b>入参类别</b>:表单参数方式,Content-Type=application/x-www-form-urlencoded
     *     <li><b>定义方式</b>: 在方法形参的位置,用Map来接收入参</li>
     *     <li><b>调用方式</b>: 入参放在表单中,且Content-Type=application/x-www-form-urlencoded</li>
     * </ul>
     */
    @RequestMapping(value = "/test4_map")
    public String test4(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

无论哪种请求方式,后端获取入参都失败!下面以PUT请求方式作为示例
下面是示例

PUT,传参失败

在这里插入图片描述

可以看到,获取不到入参!

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

先说结论,虽然在下面方法定义的,=形参中添加了MultipartFile,但是如果调用接口的时候入参类型是Content-Type=application/x-www-form-urlencoded, 那由于Content-Type=application/x-www-form-urlencoded入参类型限制了入参就无法将【文件】传过来,所以该方法的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请求方式作示例:

在这里插入图片描述

以上就是SpringBoot接收接口入参的方式小结的详细内容,更多关于SpringBoot接收接口入参的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文