全局日期请求转换处理

简介: 全局日期请求转换处理

大部分人在二三十岁上就死去了,因为过了这个年龄,他们只是自己的影子,此后的余生则是在模仿自己中度过,日复一日,更机械,更装腔作势地重复他们在有生之年的所作所为,所思所想,所爱所恨。——《约翰 • 克利斯朵夫》

我们在开发中对于日期,如果是协商好,请求发送指定格式的日期字符串

我们这边再转换成对应日期格式,如果每个都一个一个来转,非常麻烦,不方便

这里介绍一种全局的mvc转换方式

使用@InitBinder注解以及Jackson2ObjectMapperBuilderCustomizer

如果我们使用application/x-www-form-urlencoded接参,则@InitBinder上场了

只需要在Controller中写下如下代码

public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
public static final ZoneId SYSTEM_DEFAULT_ZONE_ID = ZoneId.systemDefault();
/**
 * 我还没有写描述
 *
 * @param binder 从Web请求参数到JavaBean对象的数据绑定的特殊DataBinder
 * @author <achao1441470436@gmail.com>
 * @date 2021/4/5 0005 0:48
 */
@InitBinder
protected void initBinder(WebDataBinder binder) {
    // Date 类型转换
    binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
        @Override
        @SneakyThrows
        public void setAsText(String text) {
            setValue(textToDate(text));
        }
    });
    // LocalDate类型转换
    binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
        @Override
        public void setAsText(String text) {
            setValue(textToLocalDate(text));
        }
    });
    // LocalDateTime类型转换
    binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
        @Override
        public void setAsText(String text) {
            setValue(textToLocalDateTime(text));
        }
    });
}
/**
 * 时间字符串转Date
 *
 * @param text 2021-04-04 23:10:51
 * @return java.util.Date|null
 * @author <achao1441470436@gmail.com>
 * @date 2021/4/4 0004 23:10
 */
private Date textToDate(String text) {
    return Optional.ofNullable(textToLocalDateTime(text)).map(dateTime -> dateTime.atZone(SYSTEM_DEFAULT_ZONE_ID)).map(ZonedDateTime::toInstant).map(Date::from).orElse(null);
}
private LocalDate textToLocalDate(String text) {
    return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDate.parse(str, DATE_FORMATTER)).orElse(null);
}
/**
 * 时间字符串转LocalDateTime
 *
 * @param text 2021-04-04 23:09:52
 * @return java.time.ZonedDateTime|null
 * @author <achao1441470436@gmail.com>
 * @date 2021/4/4 0004 23:05
 */
private LocalDateTime textToLocalDateTime(String text) {
    return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDateTime.parse(str, DATE_TIME_FORMATTER)).orElse(null);
}

然后你当前Controller中所有Date格式或者LocalDateTime格式的参数都可以使用yyyy-MM-dd格式传参啦

我们用下面这个实体类OrderPO

@Data
@Builder
@TableName("`order`")
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class OrderPO implements Serializable {
    private static final long serialVersionUID = 5494342842978779677L;
    /**
     * 编号
     */
    @TableId(type = IdType.INPUT)
    private Long id;
    /**
     * 发货时间
     */
    private Date sendTime;
    /**
     * 收货时间
     */
    private LocalDateTime receiveTime;
    /**
     * 保质期到期时间
     */
    private LocalDate dateProduction;
}

写一个接口测试一下

@PostMapping("testDate")
public AjaxJson testDate(OrderPO orderPO) {
    return AjaxJson.success().put("data", orderPO);
}

我们发现成功传输

这里有个弊端,这个@InitBinder只在当前Controller生效

我们想要全部Controller生效则需要写在@RestControllerAdvice

例如我们写一个全局日期转换处理器

package com.ruben.resolver;
import lombok.SneakyThrows;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.swing.text.DateFormatter;
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Optional;
/**
 * 全局日期转换处理器
 *
 * @author <achao1441470436@gmail.com>
 * @date 2021/4/5 0005 0:45
 */
@RestControllerAdvice
public class GlobalTimeResolver {
    public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_PATTERN = "yyyy-MM-dd";
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
    public static final ZoneId SYSTEM_DEFAULT_ZONE_ID = ZoneId.systemDefault();
    /**
     * 我还没有写描述
     *
     * @param binder 从Web请求参数到JavaBean对象的数据绑定的特殊DataBinder
     * @author <achao1441470436@gmail.com>
     * @date 2021/4/5 0005 0:48
     */
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        // Date 类型转换
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
            @Override
            @SneakyThrows
            public void setAsText(String text) {
                setValue(textToDate(text));
            }
        });
        // LocalDate类型转换
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) {
                setValue(textToLocalDate(text));
            }
        });
        // LocalDateTime类型转换
        binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) {
                setValue(textToLocalDateTime(text));
            }
        });
    }
    /**
     * 时间字符串转Date
     *
     * @param text 2021-04-04 23:10:51
     * @return java.util.Date|null
     * @author <achao1441470436@gmail.com>
     * @date 2021/4/4 0004 23:10
     */
    private Date textToDate(String text) {
        return Optional.ofNullable(textToLocalDateTime(text)).map(dateTime -> dateTime.atZone(SYSTEM_DEFAULT_ZONE_ID)).map(ZonedDateTime::toInstant).map(Date::from).orElse(null);
    }
    private LocalDate textToLocalDate(String text) {
        return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDate.parse(str, DATE_FORMATTER)).orElse(null);
    }
    /**
     * 时间字符串转LocalDateTime
     *
     * @param text 2021-04-04 23:09:52
     * @return java.time.ZonedDateTime|null
     * @author <achao1441470436@gmail.com>
     * @date 2021/4/4 0004 23:05
     */
    private LocalDateTime textToLocalDateTime(String text) {
        return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDateTime.parse(str, DATE_TIME_FORMATTER)).orElse(null);
    }
}

之后我们发现确实所有Controller都生效了

但有个问题,我们如果是@RequestBody接参,用的是body里的json传输数据

那么这个就不管用了。。。

这个则需要在我们的某个@Configuration中注入一个自定义的Jackson2ObjectMapperBuilderCustomizer

因为我们mvc默认使用Jackson序列化我们的参数

@Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        JavaTimeModule module = new JavaTimeModule();
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_TIME_PATTERN)));
        module.addSerializer(new LocalDateSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_PATTERN)));
        module.addSerializer(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_TIME_PATTERN)));
        return builder -> {
            builder.simpleDateFormat(GlobalTimeResolver.DATE_TIME_PATTERN);
//            builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_PATTERN)));
//            builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_TIME_PATTERN)));
            builder.modules(module);
        };
    }

我们再写个接口测试下

@PostMapping("changeOrder")
public AjaxJson changeOrder(@RequestBody OrderPO order) {
    mpOrderMapper.updateById(order);
    return AjaxJson.success();
}

可以看到成功

注意这里有个坑啊

我们必须至少在JavaTimeModule指定一个deserializer再添加进去

否则本配置则失效。。。这个还是从源码看到的

今天踩到这个坑,弄到2021-04-05 01:45:43有点无语

点进去modules查看源码

/**
 * Specify one or more modules to be registered with the {@link ObjectMapper}.
 * Multiple invocations are not additive, the last one defines the modules to
 * register.
 * <p>Note: If this is set, no finding of modules is going to happen - not by
 * Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
 * As a consequence, specifying an empty list here will suppress any kind of
 * module detection.
 * <p>Specify either this or {@link #modulesToInstall}, not both.
 * @since 4.1.5
 * @see #modules(List)
 * @see com.fasterxml.jackson.databind.Module
 */
public Jackson2ObjectMapperBuilder modules(Module... modules) {
  return modules(Arrays.asList(modules));
}

翻译过来

注意这句罪魁祸首

坑死人了!!!

相关文章
|
前端开发 数据安全/隐私保护 UED
LayUI之树形菜单(权限管理)
LayUI之树形菜单(权限管理)
577 0
|
JSON 数据格式
SpringMVC-接收请求中的json数据及日期类型参数传递
SpringMVC-接收请求中的json数据及日期类型参数传递
576 0
【开发专题_02】Executing an update/delete query
【开发专题_02】Executing an update/delete query
338 0
|
JSON 前端开发 JavaScript
【Layui】掌握的LayUI树形权限菜单,助力你的权限管理!
LayUI是一款基于jQuery的前端UI框架,而树形权限菜单是一种常见的网页导航菜单设计。LayUI树形权限菜单结合了LayUI框架的特性和树状结构的展示方式,用于实现对用户权限的管理和控制。树形权限菜单通常由多层级的树状菜单构成,每个节点表示一个功能或者页面,父节点表示上级菜单,子节点表示下级菜单。通过这种层级结构,可以清晰地展示网站或系统的功能模块之间的关系。权限管理是指根据用户的角色或权限级别对不同的用户展示不同的菜单选项。
|
安全 前端开发 Java
Spring Security系列教程05--实现Form表单认证
前言 在上一章节中,一一哥 带大家认识了Spring Security中的第一种认证方式,但是这种基本认证的方式,UI效果不漂亮,安全性也很差,好像一无是处的样子,那么有没有更好一点的认证方式呢?有的!接下来我给大家介绍一个新的认证方式,即Form表单认证。 一. Form表单认证 1. 认证方式 我们从前文中得知,Spring Security中的认证方式可以分为HTTP层面和表单层面,常见的认证方式如下: • ①. HTTP基本认证; • ②. Form表单认证; • ③. HTTP摘要认证;
729 0
|
Oracle 关系型数据库 Java
java操作多数据源将oracle数据同步达梦数据库
java操作多数据源将oracle数据同步达梦数据库
|
应用服务中间件 网络安全 nginx
Client sent an HTTP request to an HTTPS server
Client sent an HTTP request to an HTTPS server
4197 0
|
安全 Java 数据库
Spring Security 权限管理详解与案例
Spring Security 是 Spring 框架中用于提供认证和访问控制的模块。它保护了成千上万的应用程序,使其免受未经授权的访问。本文将详细介绍 Spring Security 的权限管理功能,并通过一个实际案例来展示其用法。
1739 1
|
JSON 数据格式
HttpClient封装类并接收POST请求传json参数
今天总结一下对于HttpClient的封装以及使用,便于以后的使用,这里我只封装了接收参数为Json形式的POST与GET两种方法,对于其他的方法大家可以随时留言,一起讨论。
867 0
|
算法 Java
layui实现左侧导航树形菜单
layui实现左侧导航树形菜单
1271 0
layui实现左侧导航树形菜单