ZonedDateTime as PathVariable in Spring REST RequestMapping

2022-09-04 08:28:19

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

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


答案 1

有一个用于参数的默认转换器。它使用Java 8创建如下ZonedDateTimeDateTimeFormatter

DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);

就您而言,这可能是任何或实际上任何,您的示例不会起作用。 解析和格式化为日期字符串,而不是时间戳,这是您提供的。FormatStyleDateTimeFormatterDateTimeFormatter

您可以为参数提供适当的自定义@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时间戳执行上述任何操作。执行时间戳到转换的规范方法是通过Instant#ofEpochSecond(long),给定适当的.ZonedDateTimeZoneId

long startTime = 1446361200L;
ZonedDateTime start = Instant.ofEpochSecond(startTime).atZone(ZoneId.systemDefault());
System.out.println(start);

要使此操作与 配合使用,请注册一个自定义 。类似的东西@PathVariableConverter

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带注释的类中,覆盖addFormatters@Configuration

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

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


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


答案 2

您将需要接受作为数据类型传递的内容,然后自己进行转换,您的错误日志非常清楚地告诉您这一点。@PathVariableString

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


推荐