这里有个问题可以帮助我们更好的理解 @RequestBody 的处理机制。
一、问题背景
有时候我们在写接口时,需要把前台传来的日期String类型转为Date类型。这时我们可能会用到@DateTimeFormat注解,在请求数据为非JSON格式时,这个注解是没有问题的,可用的;
但是当请求数据为JSON格式时,问题就出现了:
1、如果请求参数没有加@RequestBody注解,那么请求参数不会执行类型转换操作,数据都是默认为空(基本类型比如int = 0, 对象引用比如Date date= null)
2、如果请求参数有加@RequestBody注解,那么请求参数会执行JSON类型转换操作,但是转换会提示异常
二、案例分析
1、示例一:
- 请求方式:Post请求
- 数据格式:非JSON格式,比如form-data
- 请求资源:personPost(Person person),无@RequestBody注解
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birth;
可以看到,前台返回正常(数据无误),说明@DateTimeFormat有效,成功解析了日期字符串。
这里返回的数据都是经过@ResponseBody处理过的,因为我们没有配置返回数据的日期格式化,所以这里返回的日期格式是默认的
Person{age=1, birth=Wed Jan 01 00:00:00 CST 2020}
2、@ResponseBody对应于@RequestBody:
前者负责将Java对象序列号成JSON数据进行返回
后者负责解析请求过来的JSON数据,解析成对应的Java对象
3、示例二:
- 请求方式:Post请求
- 数据格式:JSON格式,比如application/json
- 请求资源:personPost(Person person),无@RequestBody注解
可以看到,返回数据都为空(默认的初始值),说明数据都没有传过去,不止是date,连基本类型int都没过去
Person{age=0, birth=null}
4、原因就是默认的类型转换器是没有转化成JSON格式的对应转换类的,部分转换器如下所示,(core.convert.support包)
5、解决:所以这里对应的解决办法就是,自己创建一个JSON转换器。但是实际上这个已经有实现了,只是没有触发,如下所示的构建工具(http.converter.json包),就是用来配置相关的json序列化和反序列化的
现在我们可以通过@RequestBody注解来触发,它在接收到JSON格式的数据时,会自动调用对应的JSON转换器。
下面的示例3就是这个例子:加了@RequestBody后,默认只接受application/json格式的数据,如果传入其他格式,会报415不支持的类型
6、示例三:
- 请求方式:Post请求
- 数据格式:JSON格式,比如application/json
- 请求资源:personPost(@RequestBody Person person),有@RequestBody注解
可以看到会报错,
1. nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot
deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00"
2. Cannot parse date "2020-01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX'
它并没有按照上面我们的@DateTimeFormat注解去解析,而是按照’'yyyy-MM-dd’T’HH:mm:ss.SSSX"这个格式去解析
7、解决:所以这里的解决办法就是自己定义日期格式
局部注解来解决,比如在date字段添加@JsonFormat()注解
// 这个注解用来解析JSON数据中的日期字符串,会序列化返回数据
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date birth;
三、总结
1、注解相关:
@DateTimeFormat注解:适用于请求数据为非JSON数据,不会格式化返回数据
@JsonFormat注解:适用于请求数据为JSON数据(尤其有日期数据时),且需在请求方法的参数前加@RequestBody`注解,会格式化返回数据
@RequestBody注解:解析传来的JSON数据,转换成对应的Java对象
@ResponseBody注解:转换Java对象为JSON数据,用来作为返回数据输出到前端
2、日期格式化相关:
请求非JSON数据,建议用@DateTimeFormat即可,此时不会格式化返回数据(比如get请求,当然get请求也可以请求JSON数据,只是不推荐)
请求JSON数据,建议用@ReqeustBody来转换数据,然后搭配局部注解@JsonFormat(会格式化返回数据)或者全局配置来修改默认的日期解析格式(默认"yyyy-MM-dd’T’HH:mm:ss.SSSX");全局配置也可以格式化返回数据,需配置builder.serializerByType
如果日期格式化出错,先看传来的数据是否为JSON数据(可以通过consumes来限制),然后再看有没有对于的注解或日期格式化全局配置