ZonedDateTime作为Spring REST RequestMapping中的PathVariable

时间:2015-12-21 14:48:43

标签: java spring spring-mvc

我的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吗?

或者是唯一的传递方式,然后自己进行转换?如果是这样,有一种通用的方法来处理具有类似设计的多种方法吗?

3 个答案:

答案 0 :(得分:11)

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) { ...

或使用适当的pattern和相应的日期字符串,例如

  

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

您将无法使用unix时间戳执行上述任何操作。在适当ZonedDateTime的情况下,The canonical wayZoneId转化的时间戳是Instant#ofEpochSecond(long)

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);
    }
}

并在WebMvcConfigurationSupport @Configuration注释类中注册,覆盖addFormatters

@Override
protected void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new ZonedDateTimeConverter(ZoneId.systemDefault()));
}

现在,Spring MVC将使用此转换器将String路径段反序列化为ZonedDateTime对象。

在Spring Boot中,我认为你可以为相应的@Bean声明一个Converter,它会自动注册,但不要相信我的话。

答案 1 :(得分:5)

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

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

答案 2 :(得分:1)

默认情况下,@PathVariable仅支持一组有限的类型,包括7种基本类型(booleanbyteshortint,{{ 1}},longfloat)加double

但是,您可以extend that在控制器中使用an @InitBinder并为Date添加绑定。我不确定你是否可以在外部类中定义它,但是......如果不是,你需要在每个使用ZonedDateTime.class的控制器中定义这个@InitBinder

修改:您可能需要创建自定义PropertyEditor并使用ZonedDateTime对其进行绑定才能执行此操作。