spring-cloud / spring-cloud-openfeign

Support for using OpenFeign in Spring Cloud apps
Apache License 2.0
1.2k stars 779 forks source link

Spring Boot 3.2.4 (Cloud 2023.0.1) Cannot deserialize "pageable": "INSTANCE" #1018

Closed dienarvil closed 5 months ago

dienarvil commented 5 months ago

Describe the bug

Rest Controller in server with Spring Boot 2.7.18:

image

If I consume that end point from application with Spring Boot 2.7.18 (Cloud 2021.0.5) Deserialization is correct.

If I consume that end point from application with Spring Boot 3.2.4 (Cloud 2023.0.1) Deserialization fails.

log trace:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of org.springframework.cloud.openfeign.support.PageJacksonModule$SimplePageable (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('INSTANCE')


If I create Page object with the constructor:

Page page = new PageImpl<>(build, PageRequest.of(0,2), 2);

Works perfectly in Spring 2.7.18 and Spring boot 3.2.4

Thank you !!

OlgaMaciaszek commented 5 months ago

Hello @dienarvil, thanks for reporting the issue. Please provide a minimal, complete, verifiable example that reproduces the issue.

dienarvil commented 5 months ago

demo.zip

localhost:8080/testok -> feign call to ok end point with Page page = new PageImpl<>(build, PageRequest.of(0,2), 2); and localhost:8080/testko -> feign call to ko end point with Page page = new PageImpl<>(build, Pageable.unpaged(), 2);

ttddyy commented 5 months ago

I just stumbled upon the same symptom and dug in a bit.

The reported error happens when deserializing the following json:

{
  "content": [
    "foo"
  ],
  "pageable": "INSTANCE",   <== HERE
  "totalPages": 1,
  "totalElements": 1,
  "last": true,
  "size": 1,
  "number": 0,
  "sort": [],
  "numberOfElements": 1,
  "first": true,
  "empty": false
}

As @dienarvil noted, the json is generated by the following:

Page<String> page = new PageImpl<>(List.of("foo"));  // or usage of Pageable.unpaged()
String json = this.objectMapper.writeValueAsString(page);

// deserialize json will throw the exception

When serializing a PageImpl, if it doesn't specify the pageable parameter, it uses Pageable.unpaged(). SpringDataJacksonConfiguration from spring-data-commons registers a serializer for Unpaged which writes out the value INSTANCE. So, the generated json has pageable:"INSTANCE".

It is not the json format that spring-cloud-openfeign is expecting. In v4.1.1, PageJacksonModule#SimplePageImpl added Pageable object parameter(https://github.com/spring-cloud/spring-cloud-openfeign/pull/984) and the expected format is SimplePageable. The deserializer doesn't work for INSTANCE string from Unpaged.

Per https://github.com/spring-projects/spring-data-commons/issues/3024, spring-data-commons v3.3 may produce a more stable json representation.

@dienarvil For a fix, I suggest simply providing the page information(Pageable) rather than using Unpaged while creating PageImpl object. So, that the response json will be more conformed as an added benefit.

dienarvil commented 5 months ago

@dienarvil For a fix, I suggest simply providing the page information(Pageable) rather than using Unpaged while creating PageImpl object. So, that the response json will be more conformed as an added benefit.

Thanks for the advice, yeah, with "normal" pagination from a repository the problem will never arise.

Thanks again!!

OlgaMaciaszek commented 5 months ago

Thanks for the analysis @ttddyy. Until this gets addressed better in Spring Data, we expect a proper Pageable object to be provided.