RESTful开发日期类型字段如何正确传递

目录
  1. 1. GET方法时参数传入日期类型该如何处理
  2. 2. POST方法时参数传入日期类型该如何处理
    1. 2.1. 方法一:自定义converter
    2. 2.2. 方法二:使用@JsonFormat注解
  3. 3. PUT方法时参数传入日期类型该如何处理
  4. 4. Response中日期格式该如何处理
    1. 4.1. 方法一 增加统一的messageConvert处理:
    2. 4.2. 方法二 通过@JsonFormat注解处理:
  5. 5. 总结

RESTful开发时经常会遇到参数传入日期类型及返回的日期类型值,日期和时间戳如果没有适当和一致地处理,就会给人带来头痛的问题,我这里建议大家使用统一格式化的时间字符串yyyy-MM-dd HH:mm:ss,为什么建议这个呢?这样看起来比较直观,前后端联调起来比较高效。

下面我们就细说一下日期类型的参数将如何处理。

GET方法时参数传入日期类型该如何处理

url如下:

1
http://localhost:8081/test/time_get?time=2018-07-09 10:38:57

Controller代码:

1
2
3
4
5
6
7
8
import java.util.Date;  

@RequestMapping(value = "/time_get", method = RequestMethod.GET)
@ResponseBody
public Response<Date> time_get(Date time) {
logger.info("time:{}", time);
return Response.createResponse(time);
}

在这种情况下日期参数是无法成功的传入到controller方法里,会爆出如下的异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.util.Date for value '2018-07-09 10:38:57'; nested exception is java.lang.IllegalArgumentException  
at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:81) ~[spring-core-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:35) ~[spring-core-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:178) ~[spring-core-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:161) ~[spring-beans-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93) ~[spring-beans-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64) ~[spring-beans-4.0.0.RELEASE.jar:4.0.0.RELEASE]
... 43 common frames omitted
Caused by: java.lang.IllegalArgumentException: null
at java.util.Date.parse(Date.java:615) ~[na:1.7.0_45]
at java.util.Date.<init>(Date.java:272) ~[na:1.7.0_45]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.7.0_45]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) ~[na:1.7.0_45]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.7.0_45]
at java.lang.reflect.Constructor.newInstance(Constructor.java:526) ~[na:1.7.0_45]
at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:76) ~[spring-core-4.0.0.RELEASE.jar:4.0.0.RELEASE]
... 48 common frames omitted

那如何解决上面的问题?使用@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")加到日期参数之前,像下面那样使用一样。

Controller代码:

1
2
3
4
5
6
7
8
import java.util.Date;  

@RequestMapping(value = "/time_get", method = RequestMethod.GET)
@ResponseBody
public Response<Date> time_get(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date time) {
logger.info("time:{}", time);
return Response.createResponse(time);
}

提示:年月日:pattern=”yyyy-MM-dd”,年月日时分秒:pattern=”yyyy-MM-dd HH:mm:ss”

请求体:

1
2
GET /test/time_get1?time=2018-07-09 11:31:00 HTTP/1.1  
Host: localhost:8081

后端接收到的信息,debug截图:

POST方法时参数传入日期类型该如何处理

当使用@RequestBody接受一个VO对象时@DateTimeFormat就会失效,因为我们走的是Json序列化与反序列化,@DateTimeFormat只会生效与object序列化、反序列化。

前端String转后端Date有两种方法:

方法一:自定义converter

如果使用的Spring可以自定义messageConvert或者增强MappingJackson2HttpMessageConverter中的ObjectMapper

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.text.SimpleDateFormat;  
import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
addDefaultHttpMessageConverters(messageConverters);
for (int i = 0; i < messageConverters.size(); i++) {
HttpMessageConverter<?> mc = messageConverters.get(i);
if (mc instanceof MappingJackson2HttpMessageConverter) {
ObjectMapper objectMapper = new ObjectMapper();
//当json中属性在反序列化时,javabean中没有找到属性就忽略,如果FAIL_ON_UNKNOWN_PROPERTIES=true找不到属性会报错
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//设置序列化、反序列化时日期类型的格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
((MappingJackson2HttpMessageConverter) mc).setObjectMapper(objectMapper);
}
converters.add(mc);
}
}
}

通过上面对json序列化反序列化的配置后日期参数处理就变的简单了,效果如下。

Controller代码:

1
2
3
4
5
6
@RequestMapping(value = "/time_post", method = RequestMethod.POST)  
@ResponseBody
public Response<Date> time_post(@RequestBody TestVo vo) {
logger.info("time:{}", vo.getTime());
return Response.createResponse(vo.getTime());
}

VO代码:

1
2
3
4
5
6
7
8
9
10
public class TestVo implements Serializable {  
private static final long serialVersionUID = 7435595656552442126L;
private Date time;
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}

提示:VO中无需使用@DateTimeFormat,就是一个普通的javabean即可

请求体:

1
2
3
4
5
6
POST /test/time_post HTTP/1.1  
Host: localhost:8081
Content-Type: application/json
{
"time":"2018-07-09 15:31:00"
}

后端接收到的信息,debug截图

方法二:使用@JsonFormat注解

1
2
3
4
5
6
7
8
9
10
11
public class TestVo implements Serializable {  
private static final long serialVersionUID = 7435595656552442126L;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date time;
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}

注意:使用Jackson进行json序列化反序列化,默认可以处理yyyy-MM-dd这个格式,但是反序列化后的时间会差8小时

可在SpringBoot中全局配置:

1
2
3
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8

@JsonFormat无效的原因及解决方案:
1.首先更新@JsonFormat所依赖的jar包版本,旧版本不支持对LocalDateTime类型的序列化,所以会出现不生效的问题,所以先更新版本,如果不解决,就不是这个问题
2.看是否有接口重写了FastJsonHttpMessageConverter产生了冲突

PUT方法时参数传入日期类型该如何处理

如果put传参方式与get一样在方法上直接传参(url?time=2018-07-09 10:38:57),那参考get请求参数处理方式即可

如果put传参方式与post一样使用@RequestBody传入json格式数据,那么参考post请求参数处理方式即可

请求体:

1
2
3
4
5
6
PUT /test/time_put HTTP/1.1  
Host: localhost:8081
Content-Type: application/json
{
"time":"2018-07-09 15:31:00"
}

后端接收到的信息,debug截图

前面都说的是request时日期格式处理方式,那么我们继续说一下response时日期格式如何处理。

Response中日期格式该如何处理

SpringMVC使用@ResponseBody时,日期格式默认显示为时间戳,不管方法直接返回Date类型、或者VO类型时,时间格式都一样返回时间戳,例如这样。

请求体:

1
2
3
4
5
6
POST /test/time_post1 HTTP/1.1  
Host: localhost:8081
Content-Type: application/json
{
"time":"2018-07-09"
}

响应体:

1
2
3
4
5
6
7
{  
"code": "",
"message": "",
"items": {
"time": 1531094400000
}
}

那如果我们要以字符串格式返回呢,那该如何处理?

方法一 增加统一的messageConvert处理:

如果使用的spring可以自定义messageConvert或者增强MappingJackson2HttpMessageConverter中的ObjectMapper

代码在 POST方法时参数传入日期类型该如何处理 这个章节

方法二 通过@JsonFormat注解处理:

请在VO对象的date字段上加上@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8"),例如下面代码:

VO代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class TestVo implements Serializable {  
private static final long serialVersionUID = 7435595656552442126L;

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date time;
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}

注意:@JsonFormat(pattern=”yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”) ,即可将json返回的对象为指定的类型。

返回日期格式使用的是”yyyy-MM-dd HH:mm:ss”样式字符串示例:

请求体:

1
2
3
4
5
6
POST /test/time_post1 HTTP/1.1  
Host: localhost:8081
Content-Type: application/json
{
"time":"2018-07-09 15:31:00"
}

响应体:

1
2
3
4
5
6
7
{  
"code": "",
"message": "",
"items": {
"time": "2018-07-09 15:31:00"
}
}

总结

POST请求,我们一般会用@RequestBody接收JSON对象,如果对象里面有日期时间类型数据的话,我们可以使用 @JsonFormat 注解进行格式化,它既可以对出参进行格式化,也可以对入参进行格式化

GET请求参数都是拼接在URL后面的,则需要使用 @DateTimeFormat 对入参进行格式化,放到@RequestBody修饰的对象里面是无效的