spring-cloud-openfeign icon indicating copy to clipboard operation
spring-cloud-openfeign copied to clipboard

Feign client doesn't serialize java.time.LocalDate's correctly while it present as object field

Open DmitriiVashchenko opened this issue 3 years ago • 12 comments

I'm using Spring boot 2.4.5, Spring Cloud 2020.0.3, Java 11. Is there a way to use a Feign client accepting a java.time.LocalDate as an object field where it is supposed to comply with a given format like @DateTimeFormat with some specific pattern? For instance, I have the next object which I wanna represent as query params:

@AllArgsConstructor
public class QueryRequestParams {
    @DateTimeFormat(pattern = "dd.MM.yyyy")
    private LocalDate from;
    @DateTimeFormat(pattern = "dd.MM.yyyy")
    private LocalDate to;
}

The instance of QueryRequestParams looks like:

new QueryRequestParams(LocalDate.parse("2011-12-03"), LocalDate.parse("2011-12-05"))

The feign client is:

@FeignClient(url = "http://localhost:9000")
public interface ExampleFeignClient {
    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    List<Object> getObjects(@SpringQueryMap QueryRequestParams queryRequestParams);
}

When feign client does request andQueryRequestParams` object has no null values the request look like as "http://localhost:9000?from=2011-12-03&to=2011-12-05" which has another format rather than I configured via DateTimeFormat annotation.

When I tried to use @DateTimeFormat with @RequestParam annotation just in ExampleFeignClient, for example:

@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    List<Object> getObjects(@RequestParam("from") @DateTimeFormat(pattern = "dd.MM.yyyy") LocalDate from,
                            @RequestParam("to") @DateTimeFormat(pattern = "dd.MM.yyyy") LocalDate to);

then it works as I expected. It there a way to use @SpringQueryMap with object which contains LocalDate field and that is formatted via @DateTimeFormat(pattern = "dd.MM.yyyy") annotation? Thanks for any help!

DmitriiVashchenko avatar Jul 08 '21 15:07 DmitriiVashchenko

Hi @DmitriiVashchenko,

I think your expectation works on newer version of feign. I'm using Spring boot 2.5.2, Spring Cloud 2020.0.3, Java 11.

@SpringBootApplication
@EnableFeignClients
@Slf4j
public class FeignCanApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignCanApplication.class, args);
    }

    @Autowired
    private TestClient testClient;

    @Bean
    public ApplicationRunner test() {
        return args -> {
            List<LocalDate> dateList = testClient.getObjects(new QueryRequestParams(LocalDate.parse("2011-12-03"), LocalDate.parse("2011-12-05")));
            log.info("{}", dateList);
        };
    }

    @FeignClient(name = "test", url = "http://localhost:8080")
    public interface TestClient {
        @GetMapping(value = "/get-data", produces = MediaType.APPLICATION_JSON_VALUE)
        List<LocalDate> getObjects(@SpringQueryMap QueryRequestParams queryRequestParams);
    }

    @RestController
    @Slf4j
    public static class TestController {

        @GetMapping(value = "/get-data", produces = MediaType.APPLICATION_JSON_VALUE)
        List<LocalDate> getObjects(@RequestParam("from") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate from,
                                   @RequestParam("to") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate to) {
            log.info("{} {}", from, to);
            return Arrays.asList(from, to);
        }
    }

    @AllArgsConstructor
    @Data
    public class QueryRequestParams {
        @DateTimeFormat(pattern = "dd.MM.yyyy")
        private LocalDate from;
        @DateTimeFormat(pattern = "dd.MM.yyyy")
        private LocalDate to;
    }
}

Example output: image

If I am wrong in somewhere, please share small code block so I can try to help more.

cbezmen avatar Jul 09 '21 11:07 cbezmen

Hi @cbezmen, but your date formation is also different from the pattern. I expect the date to be shaped like dd.MM.yyy when this date field is represented as an object field and I using this like:

public class QueryRequestParams {
    @DateTimeFormat(pattern = "dd.MM.yyyy")
    private LocalDate from;
    @DateTimeFormat(pattern = "dd.MM.yyyy")
    private LocalDate to;
}

@FeignClient(name = "test", url = "http://localhost:8080")
public interface TestClient {
    @GetMapping(value = "/get-data", produces = MediaType.APPLICATION_JSON_VALUE)
    List<LocalDate> getObjects(@SpringQueryMap QueryRequestParams queryRequestParams);
}

DmitriiVashchenko avatar Jul 11 '21 18:07 DmitriiVashchenko

Hi @DmitriiVashchenko

I think it is not spring-open-feign problem.

DateTimeFormat.ISOonly supports:

  • DATE
  • TIME
  • DATE_TIME
  • NONE

You can add custom conversion service. Please check this link https://www.baeldung.com/spring-date-parameters

cbezmen avatar Jul 13 '21 07:07 cbezmen

@cbezmen, but this way works fine

@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
List<Object> getObjects(@RequestParam("from") @DateTimeFormat(pattern = "dd.MM.yyyy") LocalDate from,
                        @RequestParam("to") @DateTimeFormat(pattern = "dd.MM.yyyy") LocalDate to);

Here also @DateTimeFormat annotation with LocalDate type, just presented as separate param, not field of object

DmitriiVashchenko avatar Jul 13 '21 08:07 DmitriiVashchenko

Hi @DmitriiVashchenko,

QueryRequestParams is also fail with dd.MM.yyyy because it is not a valid pattern. When you call it with wrong pattern it will throw DateTimeParseException. You have to send supported patterns of DateTimeFormat.ISO.

public class QueryRequestParams {
    @DateTimeFormat(pattern = "dd.MM.yyyy")
    private LocalDate from;
    @DateTimeFormat(pattern = "dd.MM.yyyy")
    private LocalDate to;
}

new QueryRequestParams(LocalDate.parse("13-12-2011"), LocalDate.parse("12-05-2011")) // this will throw DateTimeParseException

cbezmen avatar Jul 26 '21 10:07 cbezmen

@DmitriiVashchenko, @cbezmen is right - this will, in fact, throw an exception.

OlgaMaciaszek avatar Jul 26 '21 15:07 OlgaMaciaszek

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-cloud-issues avatar Aug 02 '21 15:08 spring-cloud-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-cloud-issues avatar Aug 09 '21 15:08 spring-cloud-issues

I have the same problem with ZonedDateTime I have a parameter

  @DateTimeFormat(pattern = "yyyyMMdd'T'HHmmss'Z'")
  private ZonedDateTime scheduledFrom;

When I declare the param as direct method param

  @GetMapping(value = "/example")
  Map<String, Object> example(@RequestParam @DateTimeFormat(pattern = "yyyyMMdd'T'HHmmss'Z'") ZonedDateTime scheduledFrom);

the formatting works OK: scheduledFrom=20220104T070640Z

However when I try using this param as a part of nested object among with @SpringQueryMap annotation

  public class WrappedScheduledFrom {
    @DateTimeFormat(pattern = "yyyyMMdd'T'HHmmss'Z'")
    private ZonedDateTime scheduledFrom;
  }
 
  @GetMapping(value = "/example")
  Map<String, Object> example(@SpringQueryMap WrappedScheduledFrom requestDto);

The formatting stops working: scheduledFrom=2022-01-04T07:06:40Z[UTC]

LMfrischtag avatar Jan 06 '22 09:01 LMfrischtag

I don't understand why has this issue been closed. What kind of feedback was expected to be provided?

From my side, I can confirm that @DateTimeFormat(pattern = "..." on @SpringQueryMap-annotated parameter is ignored by the client and resulting request consists of default ISO date format, as if @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) was given.

k0mmsussert0d avatar Mar 29 '22 15:03 k0mmsussert0d

Will take a look at it.

OlgaMaciaszek avatar Mar 29 '22 15:03 OlgaMaciaszek

I can also confirm, that feign send date as string with quotes "2022-05-25" when I use LocalDate in body's object with MediaType.MULTIPART_FORM_DATA_VALUE, for example:

@FeignClient(name = "api-client", url = "${base-url}/api", configuration = ApiConfig.class)
public interface EdoApiClient {
    @PostMapping(value = "/documents/draft", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    DocumentResponse createDraft(DocumentRequest request);
}

@Getter
@Setter
public class DocumentRequest {
    private String name;
    private MultipartFile documentFile;
    private LocalDate documentDate;
    private draft boolean;
}

public class ApiConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

from logs 

--180feeb2f1b
Content-Disposition: form-data; name="documentDate"
Content-Type: text/plain; charset=UTF-8

"2022-05-26"
--180feeb2f1b
Content-Disposition: form-data; name="draft"
Content-Type: text/plain; charset=UTF-8

true

I think feign.form.multipart.SingleParameterWriter must be applicable for LocalDate, LocalDateTime classes too.

velykov avatar May 25 '22 02:05 velykov

@FrischTag @k0mmsussert0d @velykov - sorry for not getting back to you earlier - we were concentrating on the AOT/ native, jakarta and observability portfolio-wide efforts towards the end of 2022. If you would like us to take a closer look at it, please provide a minimal, complete, verifiable example that reproduces the issue as a link to a repo to a small executable app or tests that reproduce the issue rather than separate code snippets.

OlgaMaciaszek avatar Mar 09 '23 13:03 OlgaMaciaszek

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-cloud-issues avatar Mar 16 '23 13:03 spring-cloud-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-cloud-issues avatar Mar 23 '23 13:03 spring-cloud-issues