首页 > 技术文章 > 浅析日期格式化注解@DateTimeFormat无效的问题分析深入理解@RequestBody的应用

goloving 2018-06-28 23:43 原文

  这里有个问题可以帮助我们更好的理解 @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处理过的,因为我们没有配置返回数据的日期格式化,所以这里返回的日期格式是默认的

  比如前台需按 "2020-01-01 12:00:00" 传参,后端打印及返回却都是时间格式: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来限制),然后再看有没有对于的注解或日期格式化全局配置

3、参考内容:

  @RequestBody: https://blog.csdn.net/justry_deng/article/details/80972817/

  @DateTimeFormat: https://segmentfault.com/a/1190000020423352

推荐阅读