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 读取日期为“2019-3-29”格式的路径参数?