ZonedDateTime как PathVariable в Spring REST RequestMapping
У меня есть конечная точка REST в моем весеннем приложении, которая выглядит так
@RequestMapping(value="/customer/device/startDate/{startDate}/endDate/{endDate}", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public Page<DeviceInfo> getDeviceListForCustomerBetweenDates(@PathVariable ZonedDateTime startDate, @PathVariable ZonedDateTime endDate, Pageable pageable) {
... code here ...
}
Я попытался передать переменные пути как миллисекунды, так и секунды. Однако я получаю следующее исключение в обоих случаях:
"Failed to convert value of type 'java.lang.String' to required type 'java.time.ZonedDateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.web.bind.annotation.PathVariable java.time.ZonedDateTime for value '1446361200'; nested exception is java.time.format.DateTimeParseException: Text '1446361200' could not be parsed at index 10"
Может кто-нибудь объяснить, как я могу передать (в секундах или миллисекундах) строку, такую как 1446361200, и заставить ее преобразовать в ZonedDateTime?
Или это единственный способ передать как строку, а затем сделать преобразование самому? Если да, то есть ли общий способ получить это обрабатывается для нескольких методов с аналогичным дизайном?
3 ответов:
Есть конвертер по умолчанию для параметров
ZonedDateTime. Он использует Java 8DateTimeFormatter, созданный какDateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);Это могло быть любое
FormatStyleили действительно любоеDateTimeFormatter, поскольку вы обеспокоены, ваш пример не сработал бы.DateTimeFormatterанализирует и форматирует строки даты, а не временные метки, что вы и предоставили.Вы могли бы предоставить соответствующий обычай
@org.springframework.format.annotation.DateTimeFormatк вашему параметру, такому какpublic Page<DeviceInfo> getDeviceListForCustomerBetweenDates( @PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime startDate, @PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime endDate, Pageable pageable) { ...Или с соответствующим
patternи еще ... соответствующая строка даты, например2000-10-31T01:30:00.000-05:00
Вы не сможете сделать ничего из вышеперечисленного с меткой времени unix. канонический способ выполнить преобразование timestamp в
ZonedDateTimeзаключается в следующем:Instant#ofEpochSecond(long), дано соответствующееZoneId.long startTime = 1446361200L; ZonedDateTime start = Instant.ofEpochSecond(startTime).atZone(ZoneId.systemDefault()); System.out.println(start);Чтобы сделать эту работу с
@PathVariable, регистрировать пользовательскийConverter. Что-то вродеclass ZonedDateTimeConverter implements Converter<String, ZonedDateTime> { private final ZoneId zoneId; public ZonedDateTimeConverter(ZoneId zoneId) { this.zoneId = zoneId; } @Override public ZonedDateTime convert(String source) { long startTime = Long.parseLong(source); return Instant.ofEpochSecond(startTime).atZone(zoneId); } }И зарегистрировать его в a
WebMvcConfigurationSupport@Configurationаннотированный класс, переопределениеaddFormatters@Override protected void addFormatters(FormatterRegistry registry) { registry.addConverter(new ZonedDateTimeConverter(ZoneId.systemDefault())); }Теперь Spring MVC будет использовать этот конвертер для десериализации сегмента пути
Stringв объектZonedDateTime.
В Spring Boot, я думаю, вы можете просто объявить
@Beanдля соответствующегоConverter, и он зарегистрирует его автоматически, но не верьте мне на слово.
Вам нужно будет принять то, что вы передали в
@PathVariableкак тип данныхString, а затем выполнить преобразование самостоятельно, ваши журналы ошибок довольно ясно говорят вам об этом.Библиотека Spring не может преобразовать строковые значения"1446361200" в тип
ZonedDateTimeчерез@PathVariableпривязку, к сожалению.
По умолчанию,
@PathVariableподдерживает только ограниченный набор типов, включая 7 основных типов (boolean,byte,short,int,long,float, иdouble) плюсDate.Однако можно расширить это , используя
@InitBinderв вашем контроллере и добавление привязки дляZonedDateTime.class. Хотя я не уверен, что вы можете определить это во внешнем классе... если нет, то вам нужно определить это@InitBinderв каждом контроллере, использующемZonedDateTime.Edit: возможно, Вам потребуется создать пользовательский
PropertyEditorи свяжите его с помощьюregisterCustomEditor, чтобы сделать это.
Comments