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

时间:2018-05-07 14:12:42

标签: spring-mvc spring-boot kotlin

我们的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
  7. 在我的控制器中,我使用以下注释:

    @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标准)?

2 个答案:

答案 0 :(得分:1)

您获得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)

答案 1 :(得分: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())
  }
}
相关问题