首页 > 解决方案 > 如何使用 Spring MVC @RequestParam 解析不同的 ISO 日期/时间格式

问题描述

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

  1. 2018-01-01T15:56:31.410Z
  2. 2018-01-01T15:56:31.41Z
  3. 2018-01-01T15:56:31Z
  4. 2018-01-01T15:56:31+00:00
  5. 2018-01-01T15:56:31+0000
  6. 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 标准)?

标签: spring-mvcspring-bootkotlin

解决方案


我通过添加自定义格式化程序注释来解决它:

@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 类来自我的另一个问题,如何使用 Jackson 和 java.time 解析不同的 ISO 日期/时间格式?

并使用 WebMvcConfigurer 将其添加到 Spring:

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

推荐阅读