0.引言
1) 传参的几种方式
我们从调用方的视角去看待这个问题,对调用方来说,它在调用接口时有如下的几种传参方式:
- 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 上传表单数据
- xml
- raw
- binary
2) 关于请求方式与传参方式
无论是GET
、POST
、PUT
还是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) 代码说明
- 下面所有接口的定义,使用的都是
@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
这种入参方式,入参是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
是整个请求,可以获取到所有的数据。且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
,两者都可以用来上传前端表单
数据,下面简单将两者的不同列举出来,更详细的可自行上网查询或者看下面博客 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
等请求方式,但是一般偏向于应用在 用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
接收参数 - 兼容的请求方式:
GET
POST
PUT
DELETE
- 适用场景:一般很少这种方式来接收Content-Type=application/x-www-form-urlencoded的请求
- 优点:获取入参很麻烦,没啥优点
- 缺点:获取入参很麻烦
注意:
- 如果是
GET
请求方式,则:通过request.getInputStream()
方式是可以获取入参的,获取到的数据示例:name=wangwu&age=12 ,但是通过request.getParameter("xxx")
方式,却获取不到入参! - 如果是
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-urlencoded
和form-data
它们两个的区别在上面已经说过了,这里不再赘述。
其实form-data
这种入参方式,对应的接口的定义方式,跟x-www-form-urlencoded
几乎是一样的。
3.3.1 接口定义方式1:在方法形参的位置,把每个参数都平铺开来
- 定义方式: 方法形参的位置,把每个参数都平铺开来,也可以使用
@RequestParam
注解 - 兼容的请求方式:
GET
POST
PUT
DELETE
- 适用场景:虽然这种传参方式,适用于
POST
PUT
DELETE
等请求方式,但是一般偏向于应用在 用POST
或PUT
方式去发送的表单
数据,且参数个数少的情形 - 优点:方便简单
- 缺点:参数个数如果过多,方法体结构会显得很臃肿,获取文件类型入参也很麻烦
代码
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:把入参封装到一个实体中
- 定义方式: 把入参封装到一个实体中,并且形参定义那里,一定不能使用
RequestParam
或RequestBody
修饰,否则调用方无法传参 - 兼容的请求方式:
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
请求作为示例
可以看到,参数获取到了。