SpringBoot接收接口入参的方式小结
作者:象牙酥
引言
我们从调用方的视角去看待这个问题,对调用方来说,它在调用接口时有如下的几种传参方式:
Query参数。表现形式是调用方在调用接口时,入参是拼接在接口的URI
后面的,如/test_get/requestparam_1?name=wangwu&age=18
。这种方式在入参个数比较少
的GET
请求方式中比较常用。
Path参数。这是REST
风格的路径参数
,入参直接拼接在接口的URI
里面,如/test_pathvar/test1/zhangsan/1
,其中的zhangsan
和1
就是就是参数。这种方式在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>
代码的说明
- 下面所有接口的定义,使用的都是
@RequestMapping
,且都没有指定具体的请求类型——如此故意为之就是为了使得接口从技术上
支持各种请求方式(GET
POST
PUT
DELETE
等),好方便测试在不同的请求方式下,是否支持各种不同的传参方式
。但是大家在平日的业务代码开发中,最好是在接口定义时指定特定的请求方式。 - 在下面所有的代码中,我都在方法定义的形参中加上了
HttpServletRequest
,是因为我要从HttpServletRequest
中获取请求的方法类型(request.getMethod()
)和URI(request.getRequestURI()
),从而在接口的返回结果在显现出来,以方便调试。 - 大家在自己实际的业务代码中可以根据自身需求决定是否加上这个。不加也不影响入参的接收!
- 下面所以接口的定义中,方法返回类型都是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当中。
这是因为在我的项目中,我结合@RestControllerAdvice
和ResponseBodyAdvice
,对接口返回结果进行了统一的处理。(核心的处理逻辑是:如果接口返回结果类型已经是指定的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
请求方式中 - 优点:方便简单
- 缺点:
- 调用时入参直接显示在uri中,不太安全
- 方法定义时参数个数如果过多,方法体结构会显得很臃肿
- 没有入参校验
代码
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
,可实现入参的必填校验/重命名/默认值
等简单的校验功能 - 缺点:
- 调用时入参直接显示在uri中,不太安全
- 方法定义时如果参数个数如果过多,方法体结构会显得很臃肿
@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
是整个请求,可以获取到所有的数据.且HttpServletRequest
、HttpServletResponse
都是内置对象,可以使用 - 缺点:代码中再去从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
,其中的zhangsan
和1
就是就是参数。这种方式在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参数,这种方式是把入参放在了请求体
当中!它跟前两种入参方式的最大区别,就是:
- 前两种入参方式它们的入参都是直接体现在了调用接口时候的
URI
中 - 而当前的这种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
接收
- 定义方式: 在方式形参的位置,用原生的
HttpServletRequest
接收 - 兼容的请求方式:
GET
POST
PUT
DELETE
- 适用场景:从技术上来说,这种传参方式同时适用于GET POST PUT DELETE等请求方式,但是正常情况下比较少用这种方式来获取
Content-Type=application/json
的body参数了 - 优点:
HttpServletRequest
是整个请求,可以获取到所有的数据。且HttpServletRequest
、HttpServletResponse
都是内置对象,可以使用 - 缺点:码中再去从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
等请求方式,但是正常情况下一般用在POST
或PUT
请求中 - 优点:
- 方法形参的定义非常简洁;
- 调用的时候参数放在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
等请求方式,但是正常情况下一般用在POST
或PUT
请求中 - 优点:
- 方法形参的定义非常简洁;
- 调用的时候参数放在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-urlencoded
和form-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:在方法形参的位置,把每个参数都平铺开来
- 定义方式: 方法形参的位置,把每个参数都平铺开来,也可以使用
@RequestParam
注解 - 兼容的请求方式:
POST
PUT
DELETE
,注意,不兼容GET请示方式 - 适用场景:虽然这种传参方式,适用于
POST
PUT
DELETE
等请求方式,但是一般偏向于应用在 用POST
或PUT
方式去发送的表单
数据,且参数个数少,且表单字段都是普通类型(即表单字段不是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
等请求方式,但是一般偏向于应用在 用POST
或PUT
方式去发送的表单数据,且表单字段都是普通类型(即表单字段不是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
接收参数 - 兼容的请求方式:
POST
PUT
DELETE
,注意,不兼容GET请示方式 - 适用场景:由于当请求的Content-Type=application/x-www-form-urlencoded时,只支持GET请求方式,并且即使是GET请求方式能获取到入参了,之后的校验和处理也很麻烦,所以一般不用这种方式来接收Content-Type=application/x-www-form-urlencoded的请求
- 优点:方便简单,且这接收入参的方式,直接调用方用GET的请求方式传参
- 缺点:
- 1)需要自己在代码中显式的从request的inputStream流【获取+转换+解码】数据,很麻烦
- 2)如果请求方式是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请求方式,如果使用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类型请求的入参!!!
- 定义方式: 用
Map
接收参数
代码
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对象始终为空!
- 定义方式: 方法形参的位置,把每个参数都平铺开来,也可以使用
@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
请求方式作示例:
以上就是SpringBoot接收接口入参的方式小结的详细内容,更多关于SpringBoot接收接口入参的资料请关注脚本之家其它相关文章!