title: SpringMVC —— 常用参数解析器介绍
subTitle: @RequestPart 与 @RequestParam 与 @RequestBody 注解使用的完全攻略
date: 2020/11/10 16:58
注解 | 参数解析器 | 请求 content-type | 是否使用到 MediaType | 使用场景 | 原理简述 |
---|---|---|---|---|---|
@RequestParm | RequestParamMethodArgumentResolver | 无(get请求)和multipart/form-data | 否 | get 请求,但也支持 form-data 的获取文件和参数 | 1. 代码中对 MultipartFile 做了特殊处理。2. request.getParameterValues() |
@RequestPart | RequestPartMethodArgumentResolver | multipart/form-data | 是 | 获取文件、获取 form-data 中的内容 | 如果参数列表接收的是文件,直接封装成 MultipartFile 类型,如果不是那么根据他的 content-type 解析成对象 |
@RequestBody | RequestResponseBodyMethodProcessor | application/json | 是 | 接收 json 数据 | |
@RequestBody + MultiValueMap | RequestResponseBodyMethodProcessor | application/x-www-form-urlencoded | 是 | 接收 form 表单提交的数据 |
@RequestParm 与 @RequestPart 作用场景
@RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;
@RequestPart:提供对“multipart/form-data”请求的全面支持,支持Servlet 3.0文件上传(javax.servlet.http.Part)、支持内容的HttpMessageConverter(即根据请求头的Content-Type,来判断内容区数据是什么类型,如JSON、XML,能自动转换为命令对象),比@RequestParam更强大(只能对请求参数数据绑定,key-alue格式),而@RequestPart支持如JSON、XML内容区数据的绑定;
HandlerMethodArgumentResolver
他是所有参数解析器继承的接口
public interface HandlerMethodArgumentResolver {
/**
* 判断当前参数解析器能否解析参数列表上的指定参数
*/
boolean supportsParameter(MethodParameter parameter);
/**
* 从请求中将参数解析成对象
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
RequestParm 参数解析器
代码:
// 使用 @RequestPart 接收文件和参数
@PostMapping("/v1/design/add")
public String addDesign(
@RequestPart(value = "file") MultipartFile file, // 这里也可以用 @RequestParam,但是还是推荐用 @RequestPart 以防有别的坑
@RequestParam(value = "pid") Long pid,
@RequestPart User user // 这里只能用 @RequestPart 注解
) {
return "123";
}
解析:
注:后面参数绑定器相关代码解析参见之前的文章。
@RequestPart 参数解析器
后面的流程在这篇文章中已经详细的讲了(他和@RequestBody 参数解析器调用了同一个抽象父类的方法)。
@RequestBody 参数解析器
在这篇文章中已经详细的讲了。
下面只提一个小问题:
@RequestBody 参数解析器和@RequestPart 参数解析器调用了同一个抽象父类,那么他可不可以解析本例中的 user 对象呢,反正我第一时间是觉得可以,但是经过测试发现并不可以,所以就开始看两个解析器调用抽象父类方法的入参有什么不同,玄机就在上面图中的紫字中。
Form 表单提交 —— AllEncompassingFormHttpMessageConverter 消息转换器
本来这个是消息转换器的,但是在这里顺道讲一下吧,这个是以前传统 form 表单提交时的参数转换器,对应的 content-type 为 application/x-www-form-urlencoded
,示例如下:
@PostMapping("/v1/testForm")
public String testForm(@RequestBody MultiValueMap<String, String> req) { // form 表单提交的参数必须要使用 MultiValueMap 来接收
List<String> name = req.get("name"); // 这个 map 中一个 key 可以对应多个 value
List<String> age = req.get("age");
return "456";
}
解析:
注意点,实际使用的时候(虽然这种方法一百年都可能不会使用了)接收的参数类型最好不要改他的泛型:
// 上面问题可以类比成以下代码
public static void main(String[] args) {
// 因为 java 采用的泛型擦除方式实现的泛型,所以实际上代码中是没有进行类型的限制的
Map map = new HashMap();
// 所以 spring 可以向这个 map 中注入任何类型的数据
map.put("name", Arrays.asList("123", "456"));
// 在 Spring 给我们绑定参数的时候其实也是没有泛型的
Map req = map;
// 当我们代码调用 get("name") 的时候,java 帮我们做了一次强转成 List 操作,但实际上 List 也是没有泛型的,所以此时也不会报错
List name = (List) map.get("name");
// 当调用 get(0) 方法的时候,java 帮我们做了一次强转成 Long 类型的操作,但实际上他是 String 类型,
// 所以会报出:java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
Long aLong = (Long) name.get(0);
}
本文参考
Springboot使用feign上传文件
@RequestPart 解决同时上传文件和json的解决方案
@RequestBody与Content-type