You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@RestController
public class MyController {
@PostMapping("/hello")
public String test(@RequestBody MyBeanVo... param) {
MyBeanVo vo = param[0];
return vo.getValue();
}
}
传单个参数时报错: "exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"JSON parse error: Can not deserialize instance of com.test.config.MyBean[] out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.test.config.MyBean[] out of START_OBJECT token\n at [Source: java.io.PushbackInputStream@24e6f1b2; line: 1, column: 1]"
原因:前端的参数(单个数据)无法解析为MyBean[],而这涉及到了Json的反序列化
自定义反序列化
方案一
定义一个批量实体类
package com.test.config;
import java.util.List;
public class BatchVo<T> {
List<T> list;
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
@RestController
public class MyController {
@PostMapping("/hello")
public String test(@RequestBody BatchVo<MyBeanVo>param) {
MyBeanVo vo = param.getList().get(0);
return vo.getValue();
}
}
背景
场景一:前后端对接接口,增删改查,一开始请求的参数,基本是单条数据,json格式基本是{"key":"value"},产品后续扩展,传参变成批量操作json格式为[xxx,xxx]或者[{"key":"value"}],此时后端修改原接口的接收对象为数组的话,前后端灰度发布,就会存在旧版本不兼容
场景二:产品的客户端,可能由web端,PC端,App端组成,例如当某个接口的参数结构改造为数组时,web端更新了,而App和PC端未更新,就存在不兼容其他端
解决思路
优点:不影响旧接口,影响范围小
缺点:重复的代码,后期存在无用的接口
优点:比较根本解决问题
缺点:程序员的缺点,不是所有程序员都能预先判断接口参数的类型
上代码
以下是尝试用三种方法解决以上场景的过程
定义一个接收前端的实体类MyBeanVo
可变参数(不能解决)
开始以为Java中的可变参数Object...,在调用方法时,既可以传单个参数,又可以传多个参数,但是不能解决。因为可变参数实际上是Object[]数组
传单个参数时报错: "exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"JSON parse error: Can not deserialize instance of com.test.config.MyBean[] out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.test.config.MyBean[] out of START_OBJECT token\n at [Source: java.io.PushbackInputStream@24e6f1b2; line: 1, column: 1]"
原因:前端的参数(单个数据)无法解析为MyBean[],而这涉及到了Json的反序列化
自定义反序列化
方案一
定义一个批量实体类
@JsonComponent注解会自动注入到spring中,反序列化BatchVo<MyBeanVo>时会自动执行deserialize方法,但是有个弊端,JsonDeserializer<T>的T必须是具体类型,不能携带泛型,不同参数就有不同的Vo,要针对不同的Vo都写一个自定义反序化的类就很麻烦
绑定的参数必须加@RequestBody,不然反序列化无法走MyJsonDeserializer的deserialize方法
发起请求:POST localhost:8080/hello
body参数:[{"value":"hello world"}] 或者 {"value":"hello world"}
返回皆为:hello world
分析:明显这种设计除非MyBean可以设计得很强大、很通用,可以接收前端所有的请求参数。要不然每个Vo类都需要写一个实现JsonDeserializer的反序化列解析类,或者每次都需要在contrller层做Json的再次反序列化。这样的实现变得繁琐,增加代码量
方案二
自定参数解析器自定义参数解析器
将RequestBodyArgumentResolver注册到WebMvcConfigurerAdapter当中。
定义mapping接口,在参数上加上注解@BatchBody
传入参数{"value":"hello world"}或者[{"value":"hello world"}]
返回皆为:hello world
可以完美兼容数组,集合,可变参数(实际是数组)
分析:RequestBodyArgumentResolver解析Json字符串,需要检测格式是否正确,需要兼容单个数据和批量数据的参数,只需要把该参数改成List/数组[]/可变参数,再在前面加上@BatchBody注解即可实现,service层和dao层要设计为批量的传参
总结
SpringMVC提供了很多自定义拦截/过滤器的接口和类,注册到配置类中,为开发者提供了方便的api,能满足开发中的大多数场景的需求,其扩展性真的做得很赞。同时,我们在设计一个接口,一个函数,多考虑其扩展和接入场景,让每个函数变得更健壮,先设计再编码,减少试错的成本
The text was updated successfully, but these errors were encountered: