如何使用 Spring MVC @RequestParam 解析不同的 ISO 日期/时间格式

Posted

技术标签:

【中文标题】如何使用 Spring MVC @RequestParam 解析不同的 ISO 日期/时间格式【英文标题】:How to parse different ISO date/time formats with Spring MVC @RequestParam 【发布时间】:2018-10-17 09:15:16 【问题描述】:

我们的 Rest API 被多个外部方使用。它们都使用“ISO-ish”格式,但时区偏移的格式略有不同。以下是我们看到的一些最常见的格式:

    2018-01-01T15:56:31.410Z 2018-01-01T15:56:31.41Z 2018-01-01T15:56:31Z 2018-01-01T15:56:31+00:00 2018-01-01T15:56:31+0000 2018-01-01T15:56:31+00

在我的控制器中,我使用以下注释:

@RequestMapping(value = ["/some/api/call"], method = [GET])
fun someApiCall(
  @RequestParam("from") 
  @DateTimeFormat(iso = ISO.DATE_TIME) 
  from: OffsetDateTime
) 
  ...

它可以很好地解析变体 1-4,但会为变体 5 和 6 生成 400 Bad Request 错误,但有以下异常:

Caused by: java.time.format.DateTimeParseException: Text '2018-01-01T13:37:00.001+00' could not be parsed, unparsed text found at index 23
  at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1952)
  at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)

如何让它接受所有上述 ISO 格式变体(即使它们不是 100% 符合 ISO 标准)?

【问题讨论】:

【参考方案1】:

我通过添加自定义格式化程序注释解决了这个问题:

@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class IsoDateTime

加上一个FormatterFactory:

class DefensiveDateTimeFormatterFactory : 
EmbeddedValueResolutionSupport(), AnnotationFormatterFactory<IsoDateTime> 

  override fun getParser(annotation: IsoDateTime?, fieldType: Class<*>?): Parser<*> 
    return Parser<OffsetDateTime>  text, _ -> OffsetDateTime.parse(text, JacksonConfig.defensiveFormatter) 
  

  override fun getPrinter(annotation: IsoDateTime, fieldType: Class<*>): Printer<*> 
    return Printer<OffsetDateTime>  obj, _ -> obj.format(DateTimeFormatter.ISO_DATE_TIME) 
  

  override fun getFieldTypes(): MutableSet<Class<*>> 
    return mutableSetOf(OffsetDateTime::class.java)
  

实际的 DateTimeFormat 类来自我的另一个问题,How to parse different ISO date/time formats with Jackson and java.time?

并使用 WebMvcConfigurer 将其添加到 Spring:

@Configuration
open class WebMvcConfiguration : WebMvcConfigurer 
  override fun addFormatters(registry: FormatterRegistry) 
    registry.addFormatterForFieldAnnotation(DefensiveDateTimeFormatterFactory())
  

【讨论】:

【参考方案2】:

您会得到 400-Bad Request,因为格式 5 和 6 不是 ISO 规范的一部分。

如果您查看时区指示符,它可以是 Z+hh:mm-hh:mm 之一。

ISO-8601格式的官方列表可以找到here

   Year:
      YYYY (eg 1997)
   Year and month:
      YYYY-MM (eg 1997-07)
   Complete date:
      YYYY-MM-DD (eg 1997-07-16)
   Complete date plus hours and minutes:
      YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
   Complete date plus hours, minutes and seconds:
      YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
   Complete date plus hours, minutes, seconds and a decimal fraction of a second
      YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)

地点:

     YYYY = four-digit year
     MM   = two-digit month (01=January, etc.)
     DD   = two-digit day of month (01 through 31)
     hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
     mm   = two digits of minute (00 through 59)
     ss   = two digits of second (00 through 59)
     s    = one or more digits representing a decimal fraction of a second
     TZD  = time zone designator (Z or +hh:mm or -hh:mm)

【讨论】:

我猜你是对的,但我们的客户仍然使用这些格式(可能他们使用了一些损坏的框架/库)。如果可能的话,我愿意接受所有格式,以便为我们的客户提供尽可能好的 API 体验,即使他们使用损坏的库。 您可以尝试搜索@InitBinder。这样您就可以提供自己的转换器机制。也许这篇文章可能会有所帮助。 ***.com/questions/5211323/… 根据***,格式6应该被iso8601覆盖 我不会信任***页面而不是 w3.org 已发布的文档。 我也不是,而且 W3C 也不是 ISO。真正的 ISO 标准文档不是免费提供的 :(

以上是关于如何使用 Spring MVC @RequestParam 解析不同的 ISO 日期/时间格式的主要内容,如果未能解决你的问题,请参考以下文章

spring3 mvc的service如何在类中的中的怎么样使用

spring mvc如何使用resource

如何使用 Spring Security / Spring MVC 处理表单登录

如何在 Spring-mvc 中使用 Session 属性

如何使用 spring 3.2 新 mvc 测试登录用户

如何在带有注解配置的spring mvc中使用spring数据