Spring boot GET преобразование типов данных не работает

Я получаю следующее исключение при попытке использовать загрузку Spring. Не удалось преобразовать параметр запроса. Что мне здесь не хватает? Как настроить конвертер типа данных запроса при загрузке Spring?

2018-02-23 17:05:11.868  WARN 13976 --- [nio-9988-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@io.swagger.annotations.ApiParam @org.springframework.web.bind.annotation.RequestParam java.time.LocalDate] for value '2017-07-21'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2017-07-21]


2018-02-23 17:18:29.582  WARN 13976 --- [nio-9988-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.time.OffsetDateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@io.swagger.annotations.ApiParam @org.springframework.web.bind.annotation.RequestParam java.time.OffsetDateTime] for value '2017-07-21T17:32:28Z'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2017-07-21T17:32:28Z]

Мой метод контроллера выглядит следующим образом.

@RequestMapping(value = "/mypath/status",
        produces = { "application/json" },
        method = RequestMethod.GET)
default ResponseEntity<MyStatusResponse> _getMyStatusByQuery(@RequestHeader(value="myId", required=true) Integer myId,
@Valid @RequestParam(value = "param1", required = false) String param1,
@Valid @RequestParam(value = "param2", required = false) String param2,
@Valid @RequestParam(value = "dueDateStart", required = false) LocalDate dueDateStart,
@Valid @RequestParam(value = "dueDateEnd", required = false) LocalDate dueDateEnd,
@Valid @RequestParam(value = "sentOnEnd", required = false) OffsetDateTime sentOnEnd) {
    return getMyStatusByQuery(myId, param1, param2, dueDateStart, dueDateEnd, sentOnStart, sentOnEnd);
}

Я также добавил конвертер для OffsetDateTime (в основном это генерируется swagger)

public class RFC3339DateFormat extends ISO8601DateFormat {

  // Same as ISO8601DateFormat but serializing milliseconds.
  @Override
  public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
    String value = ISO8601Utils.format(date, true);
    toAppendTo.append(value);
    return toAppendTo;
  }

}

application.properties

spring.jackson.date-format=my.test.RFC3339DateFormat
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false

Ниже мой pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.jigarshah.api</groupId>
    <artifactId>test-me</artifactId>
    <packaging>jar</packaging>
    <name>test-me</name>
    <version>1.0.0</version>
    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <springfox-version>2.8.0</springfox-version>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
    </parent>
    <build>
        <sourceDirectory>src/main/java</sourceDirectory>
    </build>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <!--SpringFox dependencies -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox-version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox-version}</version>
        </dependency>

        <!-- XML processing: Jackson -->
        <dependency>
          <groupId>com.fasterxml.jackson.dataformat</groupId>
          <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>


        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>
    <!-- Bean Validation API support -->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
        <scope>provided</scope>
    </dependency>
    </dependencies>
</project>

2 ответа

Решение

https://blog.codecentric.de/en/2017/08/parsing-of-localdate-query-parameters-in-spring-boot/

Это было правильное решение. Spring Boot 2 может иметь лучшее решение для этого.

https://github.com/spring-projects/spring-boot/commit/2fa0539e7f7bf93505f67303955cc7da6f9f5846

Вы можете достичь этого, создав классы, реализующие JsonDeserializer, а затем добавьте их в ObjectMapper Джексона.

Пример класса конфигурации для этого:

@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addDeserializer(LocalDate.class, new JsonDateDeserializer());
        simpleModule.addDeserializer(OffsetDateTime.class, new JsonDateTimeDeserializer());
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }

}

Где JsonDateDeserializer и JsonDateTimeDeserializer - это ваши классы с вашей реализацией преобразования с правильными форматами шаблонов.

Другие вопросы по тегам