micronaut 将请求参数转换为 Instant 或 ZonedDateTime

micronaut convert request param to Instant or ZonedDateTime

我正在使用 Micronaut 1.3.4,我正在尝试将输入转换为 Instant。到目前为止,我尝试了很多方法但没有成功。以下代码仅显示了我尝试过但均无效的两种情况。

我想将输入日期转换为 InstantOffsetDateTime

    @Get("/...")
    public List<Obj> method(
//            @Format("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
//                    Instant startDateTime,
            @Format("yyyy-MM-dd")
                    LocalDate endDateTime
    ) {

如果可能,我想使用 ISO 8601。

我试过的输入:

2013-02-04
2013-02-04T22:44:30.652Z
...

我得到的输出是:

"message": "Required argument [LocalDate endDateTime] not specified",

我的后备方案是:

        String startDateTime

        Instant parse = Instant.parse(startDateTime);
        OffsetDateTime parse = OffsetDateTime.parse(startDateTime);

但我还是想知道如何用 Micronaut 来做。

下面是一个示例,说明如何在 Micronaut 中将 LocalDateTime 作为查询参数传递:

@Get("/local")
public String local(@Format("yyyy-MM-dd'T'HH:mm:ss") @QueryValue LocalDateTime time) {
    return "Time " + DateTimeFormatter.ISO_DATE_TIME.format(time);
}

示例 curl 调用结果:

$ curl http://localhost:8080/example/local?time=2020-04-13T21:13:59
Time 2020-04-13T21:13:59

当时区始终相同时,您可以使用 LocalDateTime。你可以把它转换成 Instant 像这样 time.atZone(ZoneId.systemDefault()).toInstant().

当时区可以不同并且必须是输入时间的一部分时,那么你可以像这样使用ZonedDateTime参数:

@Get("/zoned")
public String method(@Format("yyyy-MM-dd'T'HH:mm:ss.SSSZ") @QueryValue ZonedDateTime time) {
    return "Time " + DateTimeFormatter.ISO_DATE_TIME.format(time);
}

转换成 Instant 很简单:time.toInstant()

示例 curl 调用结果:

$ curl http://localhost:8080/example/zoned?time=2020-04-13T21:13:59.123-0100
Time 2020-04-13T21:13:59.123-01:00

当您以错误的格式输入时间查询参数(此处缺少秒数和时区偏移)时,它的行为如下:

$ curl http://localhost:8080/example/zoned?time=2020-04-13T21:13:59
{"message":"Required QueryValue [time] not specified","path":"/time","_links":{"self":{"href":"/example/zoned?time=2020-04-13T21:13:59","templated":false}}}

您也可以使用时间作为请求路径的一部分:

@Get("/local/{time}")
public String local(@Format("yyyy-MM-dd'T'HH:mm:ss") LocalDateTime time) {
    return "Time from request path " + DateTimeFormatter.ISO_DATE_TIME.format(time);
}

那么示例 curl 调用将是:

$ curl http://localhost:8080/example/local/2020-04-13T21:13:59
Time from request path 2020-04-13T21:13:59

当您以错误的格式在路径中输入时间(此处不需要几分之一秒)时,它的行为如下:

$ curl http://localhost:8080/example/local/2020-04-13T21:13:59.111
{"message":"Failed to convert argument [time] for value [2020-04-13T21:13:59.111] due to: Text '2020-04-13T21:13:59.111' could not be parsed, unparsed text found at index 19","path":"/time","_links":{"self":{"href":"/example/local/2020-04-13T21:13:59.111","templated":false}}}

还有另一种方法可以做到这一点。 @Format 注释有问题,因为它不能支持 java class 等 OffsetDateTime 和 ZonedDateTime 支持的所有格式。

如果你有例如这个方法:

@Get("/local")
public String local(@QueryValue OffsetDateTime time) {
    return "Time " + time.toString();
}

要使其支持 ISO8601,请修改您的 Application.java(或创建一个新的 class),其中包含:

@Context
public class Application {

    public static void main(String[] args) {
        // starts micronaut
    }

    @PostConstruct
    public void addPathConverter(ConversionService<DefaultConversionService> conversionService) {
        conversionService.addConverter(String.class, OffsetDateTime.class, (in) ->
                OffsetDateTime.parse(in));
    }
}

在 Micronaut 3 中推荐的解决这个问题的方法,具有以下灵活性 多种格式,通过添加自定义类型转换器。

OffsetDateTime 的示例实现可以是:

@Singleton
public class OffsetDateTimeConverter implements TypeConverter<String, OffsetDateTime> {

    @Override
    public Optional<OffsetDateTime> convert(String inputDateTime, Class<OffsetDateTime> targetType, ConversionContext context) {
        if (inputDateTime != null) {
            try {
                var offsetDateTime = OffsetDateTime.parse(inputDateTime);
                return Optional.of(offsetDateTime);
            } catch (DateTimeParseException e) {
                context.reject(inputDateTime, e);
            }
        }

        return Optional.empty();
    }
}

这可以在 3.2.7 documentation(和其他)中查看。