在Spring REST RequestMapping中,ZonedDateTime作为PathVariable

13

我在我的Spring应用程序中有一个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?

还是说唯一的方法是将其作为字符串传递,然后自己进行转换?如果是这样的话,是否有通用的方法来处理多个具有类似设计的方法?


ZonedDateTime是相对较新的,Spring可能尚未直接支持它。请参考JasonZ的答案以获取解决此问题的方法。 - Powerlord
3个回答

18
“ZonedDateTime”参数有一个默认的转换器。它使用Java 8的“DateTimeFormatter”创建,如下所示:
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) { ...

或者使用适当的 模式 和相应的日期字符串,如:

2000-10-31T01:30:00.000-05:00

你无法使用 Unix 时间戳完成以上任何操作。将时间戳转换为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);
    }
}

在一个带有@Configuration注解的类中注册它,继承WebMvcConfigurationSupport并重写addFormatters方法。
@Override
protected void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new ZonedDateTimeConverter(ZoneId.systemDefault()));
}

现在,Spring MVC 将使用此转换器将 String 路径段反序列化为 ZonedDateTime 对象。
在Spring Boot中,我认为你只需要声明一个相应的Converter@Bean,它就会自动注册,但不要完全相信我的话。

5

您需要将传递给@PathVariable的内容接受为String数据类型,然后自行进行转换,您的错误日志已经很清楚地告诉了您。

不幸的是,Spring库无法通过@PathVariable绑定将字符串值“1446361200”转换为ZonedDateTime类型。


你在说该做什么方面是正确的,但这是关于Spring转换而不是Jackson。 - chrylis -cautiouslyoptimistic-

1
默认情况下,@PathVariable 只支持一组有限的类型,包括 7 种基本类型 (boolean, byte, short, int, long, float, 和 double) 加上 Date
然而,在您的控制器中使用 @InitBinder 并添加 ZonedDateTime.class 的绑定,可以扩展这个限制。我不确定是否可以在外部类中定义此内容,如果不能,则需要在使用 ZonedDateTime 的每个控制器中定义此 @InitBinder
编辑:您可能需要创建一个自定义的 PropertyEditor 并使用 registerCustomEditor 进行绑定才能完成此操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接