LocalDateTime接收前端日期参数差8个小时、无法解析、返回前端带T等疑难杂症解决方案
新项目里使用LocalDateTime遇到了一些坑,总结一下,本文章以全局配置方式解决了以下问题ヽ( ̄▽ ̄)ノ:0、**概念介绍**:前端参数**反序列化**到后端,后端参数**序列化**到前端,具体概念请咨询战略合作伙伴(ノ゚∀゚)ノ [百度一下](https://www.baidu.com/)1、数据库和后端的日期差:请在数据库链接上加上&serverTimezone=Asia/Shangha
新项目里使用LocalDateTime遇到了一些坑,总结一下,本文章以全局配置方式解决了以下问题ヽ( ̄▽ ̄)ノ:
0、概念介绍:前端参数到后端先反序列化解析后执行controller内方法,后端数据出controller先序列化后给前端,具体相关概念请咨询战略合作伙伴(ノ゚∀゚)ノ 百度一下
1、数据库和后端的日期差:请在数据库链接上加上&serverTimezone=Asia/Shanghai。
2、后端无法接收数据库参数:大概率是数据库连接池包版本太低了,不支持LocalDateTime,我的是druid,看下边4.的包版本。
3、前端post请求到后端参数无法解析、差8个小时,看下边的1.-4.的反序列化
4、返回前端带T等各种奇葩格式统一格式化,看下边1.-4.的序列化
5、前端get请求到后端参数无法解析,看下边5.
6、讨厌全局配置?保证包的新鲜度下,看下边6.
7、时间戳的话,请改写反序列化方法~~我没用到哦 (=´ω`=)
2022-02-21新增:
8、遇到了个超级奇葩问题:后端远程访问后端接口,以实体类list做参数,同一个实体类list接,不知道由于有空值参数还是啥原因,在接收的时候LocalDateTime那个字段值丢失了,而且还会带空值进入反序列化方法里,这里给一个解决方法:用 com.alibaba.fastjson 的JSON.parseObject(JSON.toJSONString(你的实体类列表), List.class)作为参数发送出去,它会把所有null都干掉而且丢参问题就没有了。
2022-03-08新增:
9、综合8经测试发现:RestTemplate远程访问接收返回的数据的时候并不走咱们自定义的反序列化配置,因为咱们没告诉它走。。解决方案见新帖吧,因为把整个方法改写了一下:LocalDateTime无法解析新的解决方案
后续还有的话,再更新。
如果有问题或者有问题,欢迎留言!我会及时更新和回复哒(^_−)☆~
- LocalDateFormatConfig.java --将序列化和反序列化丢到jackson里
package com.zoomlgd.config;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* @author DepengZhang
* @date 2021年12月03日 18:41
*/
@Configuration
public class LocalDateFormatConfig {
/**
* Date格式化字符串 yyyy-MM-dd
*/
private static final String DATE_FORMAT = DatePattern.NORM_DATE_PATTERN;
/**
* Time格式化字符串 HH:mm:ss
*/
private static final String TIME_FORMAT = DatePattern.NORM_TIME_PATTERN;
@Bean
@Primary
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer())
.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)))
.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer())
.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
};
}
}
- LocalDateTimeDeserializer.java --反序列化定制类
package com.zoomlgd.config;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* @author DepengZhang
* @date 2021年12月11日 0:08
*/
public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
/**
* DateTime格式化字符串 yyyy-MM-dd HH:mm:ss
*/
private static final String DATETIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;
private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
private static final ZoneId SYSTEM_ZONE_ID = ZoneId.of("Asia/Shanghai");
private static final String T = "T";
private static final String Z = "Z";
public LocalDateTimeDeserializer() {
super(LocalDateTime.class);
}
@Override
public LocalDateTime deserialize(final JsonParser parser,
final DeserializationContext context) throws IOException {
String value = parser.getValueAsString();
// 处理标准格式的时间
if (StringUtils.isNotBlank(value) && value.length() == DATETIME_FORMAT.length()) {
if (!value.contains(T) || !value.endsWith(Z)) {
StringBuilder str = new StringBuilder(value);
str.replace(10, 11, T);
str.append(Z);
return LocalDateTime.ofInstant(Instant.parse(str.toString()), DEFAULT_ZONE_ID);
}
}
// 处理UTC格式的时间
return LocalDateTime.ofInstant(Instant.parse(value), DEFAULT_ZONE_ID)
.atZone(DEFAULT_ZONE_ID).withZoneSameInstant(SYSTEM_ZONE_ID).toLocalDateTime();
}
}
- LocalDateTimeSerializer.java --序列化定制
package com.zoomlgd.config;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
/**
* @author DepengZhang
* @date 2021年12月11日 0:03
*/
public class LocalDateTimeSerializer extends StdSerializer<LocalDateTime> {
/**
* DateTime格式化字符串 yyyy-MM-dd HH:mm:ss
*/
private static final String DATETIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;
public LocalDateTimeSerializer() {
super(LocalDateTime.class);
}
@Override
public void serialize(final LocalDateTime value,
final JsonGenerator generator,
final SerializerProvider provider) throws IOException {
if (value != null) {
generator.writeString(value.format(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
} else {
generator.writeNull();
}
}
}
- 使用的包,这里多了一个druild包,因为低版本的不支持LocalDateTime
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.17</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.18</version>
</dependency>
- GlobalExceptionHandler.java --为了支持Get的参数,还需要加上一个专门的配置方法
// 2021-12-29添加判空逻辑- -之前忘记加了
package com.zoomlgd.config;
import cn.hutool.core.date.DatePattern;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import java.beans.PropertyEditorSupport;
import java.time.*;
import java.time.format.DateTimeFormatter;
/**
* @author DepengZhang
* @date 2021年12月23日 17:06
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* Date格式化字符串 yyyy-MM-dd
*/
private static final String DATE_FORMAT = DatePattern.NORM_DATE_PATTERN;
/**
* Time格式化字符串 HH:mm:ss
*/
private static final String TIME_FORMAT = DatePattern.NORM_TIME_PATTERN;
/**
* DateTime格式化字符串 yyyy-MM-dd HH:mm:ss
*/
private static final String DATETIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;
private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
private static final ZoneId SYSTEM_ZONE_ID = ZoneId.of("Asia/Shanghai");
private static final String T = "T";
private static final String Z = "Z";
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.isNotBlank(text)) {
setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern(DATE_FORMAT)));
} else {
setValue(null);
}
}
});
binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.isNotBlank(text)) {
if (text.length() == DATETIME_FORMAT.length()) {
if (!text.contains(T) || !text.endsWith(Z)) {
StringBuilder str = new StringBuilder(text);
str.replace(10, 11, T);
str.append(Z);
setValue(LocalDateTime.ofInstant(Instant.parse(str.toString()), DEFAULT_ZONE_ID));
}
} else {
setValue(LocalDateTime.ofInstant(Instant.parse(text), DEFAULT_ZONE_ID)
.atZone(DEFAULT_ZONE_ID).withZoneSameInstant(SYSTEM_ZONE_ID).toLocalDateTime());
}
} else {
setValue(null);
}
}
});
binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.isNotBlank(text)) {
setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern(TIME_FORMAT)));
} else {
setValue(null);
}
}
});
}
}
- 如果你不想用全局配置解决以上问题,可以参考这里ヾ(✿゚▽゚)ノ
- yml配置文件全局序列化返回格式,使用上边全局配置应该是失效的,感兴趣可以自己测试一下。
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
- 在实体类字段上加注释,DateTimeFormate管反序列化,JsonFormat管序列化。
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime yourDate;
更多推荐
所有评论(0)