今天再写RestFul
在写接口时,需要把前台传来的日期String
类型转为Date
类型,无论如何都不能完成日期格式的转换,直接报错,还是自己技艺不够高超,遂以这篇文章进行记录。
想到前台传来的字符串日期格式转成实体的日期格式,这时我们可能会用到@DateTimeFormat
注解
在请求数据为非JSON
格式时,这个注解是没有问题的,可用的;
但是当请求数据为JSON
格式时,问题就出现了
@RequestBody
注解,那么请求参数不会执行类型转换操作,数据都是默认为空(基本类型比如int = 0
, 对象引用比如Date date= null
)@RequestBody
注解,那么请求参数会执行JSON
类型转换操作,但是转换会提示异常本文分三步走,如下所示,其中会穿插着介绍@DateTimeFormat
、@RequestBody
、@JsonFormat
注解
AnnationApplication.java
:主程序兼控制器
@SpringBootApplication
@RestController
public class AnnationApplication {
public static void main(String[] args) {
SpringApplication.run(AnnationApplication.class, args);
}
@PostMapping("/personPost")
public Person personPost(Person person){
System.out.println(person);
return person;
}
}
Person.java 实体类
public class Person {
private int age;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birth;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", birth=" + birth +
'}';
}
// 省略getter/setter
}
这里我们用的是PostMan
进行测试,请求示例如下
所有示例全程都有@DateTimeFormat
注解
示例1:
Post
请求JSON
格式,比如form-data
personPost(Person person)
,无@RequestBody
注解具体请求内容和返回结果如下所示
可以看到,前台返回正常(数据无误),说明@DateTimeFormat
有效,成功解析了日期字符串
这里返回的数据都是经过@ResponseBody
处理过的,因为我们没有配置返回数据的日期格式化,所以这里返回的日期格式是默认的
@ResponseBody
对应于@RequestBody
;
Java
对象序列号成JSON
数据进行返回JSON
数据,解析成对应的Java
对象我们再来看下后台,打印如下:
Person{age=1, birth=Wed Jan 01 00:00:00 CST 2020}
可以看到,后台打印正常(数据无误,日期格式忽略,因为这里的date.toString
用的Date
的默认方法)
从上面的结果我们可以看到,@DateTimeFormat
只是负责解析传来的日期字符串,转为对应的日期对象;
但是并不会修改原有的日期对象的格式(从前台返回和后台输出可以看到,日期格式不受@DateTimeFormat
的影响)
示例2:
Post
请求JSON
格式,比如application/json
personPost(Person person)
,无@RequestBody
注解具体请求内容和返回结果如下所示
可以看到,返回数据都为空(默认的初始值),说明数据都没有传过去,不止是date
,连基本类型int
都没过去
我们再来看下后台,打印如下
Person{age=0, birth=null} // 跟前台返回的数据一致
可以看到,后台解析到的数据也是空的,所以上面返回的当然是空的
原因就是默认的类型转换器是没有转化成JSON
格式的对应转换类的,部分转换器如下所示,(core.convert.support包)
解决:所以这里对应的解决办法就是,自己创建一个JSON
转换器
但是实际上这个已经有实现了,只是没有触发,如下所示的构建工具(http.converter.json包)
,就是用来配置相关的json
序列化和反序列化的
现在我们可以通过@RequestBody
注解来触发,它在接收到JSON
格式的数据时,会自动调用对应的JSON
转换器
下面的示例3就是这个例子
加了@RequestBody
后,默认只接受application/json
格式的数据,如果传入其他格式,会报415
不支持的类型
示例3:
Post
请求JSON
格式,比如application/json
personPost(@RequestBody Person person)
,有@RequestBody
注解具体请求内容和返回结果如下所示
可以看到,报错了,提示400
,这种一般属于客户端错误(比如数据格式不正确,数据过大等)
我们再来看下后台,打印如下
2021-05-15 13:48:41.578 WARN 38426 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot
deserialize value of type `java.util.Date` from String "2020-01-01 00:00:00": not a valid
representation (error: Failed to parse Date value '2020-01-01 00:00:00': Cannot parse date "2020-
01-01 00:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails
(leniency? null));
这里我们提取关键的部分来看:
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'
首先这里跟示例2不同,这里起码做了尝试转换,只是没有找到对应的格式,所以转换失败了
可以看到,它并没有按照上面我们的@DateTimeFormat
注解去解析,而是按照’'yyyy-MM-dd’T’HH:mm:ss.SSSX"
这个格式去解析
这里如果想投机的话,可以在前台直接传入’'yyyy-MM-dd’T’HH:mm:ss.SSSX’
格式的数据,如下:
但是这种办法对于前端很不友好(极其不好)
所以下面还是给出正常的解决办法
解决:所以这里的解决办法就是自己定义日期格式
date
字段添加@JsonFormat()
注解// 这个注解用来解析JSON数据中的日期字符串,会序列化返回数据
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date birth;
局部的特点:灵活,但是配置繁琐,不统一(每个字段都要加)
Jackson2ObjectMapperBuilderCustomizer
,然后自定义日期反序列化格式@Configuration
public class MyDateConvertCustoms implements Jackson2ObjectMapperBuilderCustomizer {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
// 覆盖默认的Date反序列化,第一个参数为需要反序列化的类,第二个为具体的序列化格式
jacksonObjectMapperBuilder.deserializerByType(
Date.class
,new DateDeserializers.DateDeserializer(
DateDeserializers.DateDeserializer.instance
, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
, null));
}
}
全局的特点:不灵活,但是直观清晰,配置统一
主要根据请求的数据类型来对比
JSON
数据,建议用@DateTimeFormat
即可(比如get
请求,当然get
请求也可以请求JSON
数据,只是不推荐)JSON
数据,建议用@ReqeustBody
来转换数据,然后搭配局部注解@JsonFormat
或者全局配置来修改默认的日期解析格式(默认"yyyy-MM-dd’T’HH:mm:ss.SSSX"
)注解相关:
@DateTimeFormat
注解:适用于请求数据为非JSON
数据,不会格式化返回数据@JsonFormat
注解:适用于请求数据为JSON
数据(尤其有日期数据时),且需在请求方法的参数前加`@RequestBody``注解,会格式化返回数据@RequestBody
注解:解析传来的JSON
数据,转换成对应的Java
对象@ResponseBody
注解:转换Java
对象为JSON
数据,用来作为返回数据输出到前端日期格式化相关:
JSON
数据,建议用@DateTimeFormat
即可,此时不会格式化返回数据(比如get
请求,当然get
请求也可以请求JSON
数据,只是不推荐)JSON
数据,建议用@ReqeustBody
来转换数据,然后搭配局部注解@JsonFormat
(会格式化返回数据)或者全局配置来修改默认的日期解析格式(默认"yyyy-MM-dd’T’HH:mm:ss.SSSX"
);全局配置也可以格式化返回数据,需配置builder.serializerByType
JSON
数据(可以通过consumes
来限制),然后再看有没有对于的注解或日期格式化全局配置