Как анализировать различные форматы даты / времени ISO с помощью Spring MVC @RequestParam - PullRequest
0 голосов
/ 07 мая 2018

Наш 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, но выдает ошибку 400 Bad Request для вариантов 5 и 6 со следующим исключением:

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 голосов
/ 07 мая 2018

Я решил это, добавив пользовательскую аннотацию форматера:

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

Плюс Фабрика Formatter:

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 исходит из моего другого вопроса, Как анализировать различные форматы даты / времени ISO с помощью Jackson и java.time?

И добавил его в Spring, используя WebMvcConfigurer:

@Configuration
open class WebMvcConfiguration : WebMvcConfigurer {
  override fun addFormatters(registry: FormatterRegistry) {
    registry.addFormatterForFieldAnnotation(DefensiveDateTimeFormatterFactory())
  }
}
0 голосов
/ 07 мая 2018

Вы получаете 400-Bad Request, потому что формат 5 и 6 не являются частью ISO спецификаций.

Если вы посмотрите на указатель часового пояса, он может быть одним из Z или +hh:mm или -hh:mm.

Официальный список ISO-8601 форматов можно найти здесь .

   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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...