Spring MVC LocalDate 参数格式错误

Posted

技术标签:

【中文标题】Spring MVC LocalDate 参数格式错误【英文标题】:Spring MVC LocalDate parameter has wrong format 【发布时间】:2017-12-12 03:02:41 【问题描述】:

我正在使用 Spring Boot 1.5.3、Spring Data REST、Spring JPA、Hibernate。 我在服务器中使用 java.time.* 以 UTC 工作,我的客户也应该以 UTC 发回日期。 我自定义了一点我的 REST 配置:

@Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter 

@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() 
    return new Jackson2ObjectMapperBuilderCustomizer() 

        @Override
        public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) 

            jacksonObjectMapperBuilder.serializers(InstantSerializer.INSTANCE);
            jacksonObjectMapperBuilder.serializers(new ZonedDateTimeSerializer(ISO_FIXED_FORMAT));
            jacksonObjectMapperBuilder
                    .serializers(new LocalDateSerializer(new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd").toFormatter()));
            jacksonObjectMapperBuilder.serializers(new LocalDateTimeSerializer(
                    new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").toFormatter()));
        

    ;

我在存储库中创建了一个自定义方法:

@Transactional
@PreAuthorize("isAuthenticated()")
public interface DailyCodeRepository extends PagingAndSortingRepository<DailyCode, Long> 


    @Query("SELECT d FROM DailyCode d WHERE (:code IS NULL OR code=:code) AND (:from IS NULL OR date>=:from) AND (:to IS NULL OR date<=:to)")
    public Page<DailyCode> findAllWithParameter(@Param("code") @RequestParam(value = "code", required = false) String code,
            @Param("from") @RequestParam(value = "from", required = false) @DateTimeFormat(iso=ISO.DATE) LocalDate from,
            @Param("to") @RequestParam(value = "to", required = false) @DateTimeFormat(iso=ISO.DATE) LocalDate to, Pageable pageable);

第一个问题是该方法在我添加注释@DateTimeFormat(iso=ISO.DATE) 时接受 ISO 格式,否则它会选择我的语言环境(意大利)的格式。我想将其设置为全局响应(请参阅我的 Jackson2ObjectMapperBuilderCustomizer)。

第二个问题是从客户端发送的日期参数被解释为 1 天前,所以这样的请求:

http://localhost:8080/api/v1/dailyCodes/search/findAllWithParameter?from=2017-07-07&to=2017-07-07

在数据库上引起这个查询:

select dailycode0_.`id` as id1_7_, dailycode0_.`created_by` as created_2_7_, dailycode0_.`created_date` as created_3_7_, dailycode0_.`last_modified_by` as last_mod4_7_, dailycode0_.`last_modified_date` as last_mod5_7_, dailycode0_.`sid` as sid6_7_, dailycode0_.`version` as version7_7_, dailycode0_.`code` as code8_7_, dailycode0_.`date` as date9_7_ from `daily_code` dailycode0_ where (null is null or dailycode0_.`code`=null) and ('2017-07-06' is null or dailycode0_.`date`>='2017-07-06') and ('2017-07-06' is null or dailycode0_.`date`<='2017-07-06') limit 20

所以它在 1 天前查询它是错误的。我想这是一个时区问题,但我不知道如何解决。

这是我的属性文件的相关部分:

spring.mvc.date-format= `yyyy-MM-dd`
# REST
spring.data.rest.default-page-size= 20
spring.data.rest.base-path=/api/v1
spring.data.rest.enable-enum-translation=true

#Jackson

# to avoid an error loading lazy objects
spring.jackson.serialization.fail-on-empty-beans=false
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.mapper.infer-property-mutators=false
spring.jpa.properties.hibernate.jdbc.time_zone = UTC

【问题讨论】:

【参考方案1】:

您的代码中有几个问题:

第一个问题是该方法只接受ISO格式

您需要一个OffsetDateTime(不是ZonedDateTime,阅读this)和自定义转换器。阅读this。

从客户端发送的日期参数被解释为 1 天前

为什么?您将此日期解释为哪个时区?您的本地时区可能与服务器的不同,也可能与客户端的不同。

Jackson2ObjectMapperBuilderCustomizer

这是干什么用的?

spring.mvc.date-format=yyyy-MM-dd

这不应该是必要的,而且我认为无论如何都不会对 Spring 数据做任何事情。

【讨论】:

该方法应该只接受没有时间信息的日期。在这种情况下我不需要那个。我正在使用 Spring Data REST 工具,因此没有实现该方法。服务器时区是欧洲/罗马,但由于显示的配置,我正在工作并将日期以 UTC 格式存储在数据库中。关于 Jackson2ObjectMapperBuilderCustomizer 根据github.com/spring-projects/spring-boot/issues/4217,我编辑了添加类的问题 @drenda 您显然需要将客户端发送的字符串转换为日期时间,无论您发送什么到findAllWithParameter。如果您盲目地将客户端发送的字符串传递给方法,那么您假设所有客户端都来自同一位置,这显然是错误的。然后是findAllWithParameter和DB时间之间的转换问题,你说的是UTC。如果需要,Spring Data 方法可以具有主体。在这种情况下,您将需要一个自定义存储库。 因为使用 UTC 是最佳实践,我的客户端(在本例中为 Angular 应用程序)将以 UTC 格式发送所有日期。所以我认为没有必要为一个应该非常标准的东西编写大量样板代码。谢谢 @drenda 如果日期时间处理“相当标准”,他们就不会在 JDK 8 中重写代码。此外,您的问题中缺少的重要信息是您依赖于客户端进行从本地时间到 UTC 的转换。不管怎样,听起来你已经找到了你来这里的目的。 @drenda 让我们重新讨论一下。 1. Spring数据中不能全局设置日期格式。 2. 就像我之前多次说过的那样,您的方法findAllWithParameter 采用LocalDate,而不是UTC,您的客户端发送一个字符串,您将字符串转换为LocalDate,这就是错误所在。如果您在意大利,LocalDate 显然不是 UTC。您需要使用时区将客户端字符串转换为 UTC。

以上是关于Spring MVC LocalDate 参数格式错误的主要内容,如果未能解决你的问题,请参考以下文章

最强总结:Spring MVC 接收请求参数的所有方式!

通过 Spring MVC 中的配置验证格式参数

如何让 Spring MVC 读取日期为“2019-3-29”格式的路径参数?

spring mvc接收参数方式,json格式返回请求数据

springboot~统一处理日期请求参数java.utils.Date和java.time.LocalDate

Spring JPA Equal条件与LocalDate的派生查询不起作用