Spring Boot 2.1.6 Spring Cloud OpenFeign 2.1.2 Java 11

Hi there!

Looking at the Spring Cloud OpenFeign source code looks like it is able to decode Page responses, but something isn't working as expected. I have the following client:

@RequestMapping(method = RequestMethod.POST, value = "users/find")
Page<UserResponse> findUsers(List<Long> usersId);

The users/find endpoint returns a org.springframework.data.domain.Page response, which should be decoded to Page<UserResponse>, but instead I get this exception:

Type definition error: [simple type, class org.springframework.data.domain.Page];
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `org.springframework.data.domain.Page`

But shouldn't this project handle this through PageJacksonModule.java and FeignClientsConfiguration.java?

The FeignClientsConfiguration is beaning the return new PageJacksonModule() class during application startup, and PageJacksonModule has the implementation that is needed to decode a response to Page through:

delegate = new PageImpl<>(content, PageRequest.of(number, size),
    totalElements);

I didn't find any instructions in the documentation regarding the Page desserialization. Am I missing something?

Best regards, Rafael Pacheco.

Comment From: poznachowski

I've encountered same thing. From what I understand:

  1. FeignClientsConfiguration config is not a usual configuration class. It configures separate ApplicationContext dedicated only for Feign clients (see javadoc of FeignContext class)
  2. Deserialization happens with Jackson's HttpMessageConverter and its injected from the outside applicationContext: https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java#L62
  3. Outside (normal) application context does not know about PageJacksonModule hence Jackson can't work it out.

That saying, simplest fix is to add PageJacksonModule module bean to your normal application context. To verify you can check if the module is loaded here: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java#L322

Comment From: OlgaMaciaszek

Thanks @poznachowski . @rafaelrenanpacheco could you please try this solution and let us know if it fixes the issue?

Comment From: rafaelrenanpacheco

Hello! I am on vacation and will be able to test this solution later this month.

Best regards, Rafael Pacheco.

Comment From: spring-projects-issues

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.

Comment From: spring-projects-issues

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.

Comment From: rafaelrenanpacheco

As suggested by @poznachowski, manually beaning the PageJacksonModule will enable Page<T> decode on Spring Boot :tada:

Here is the configuration I used to achieve this:

@Configuration
public class FeignDecodeConfiguration {

    @Bean
    public Module pageJacksonModule() {
        return new PageJacksonModule();
    }
}

IMO this should be the default behavior for Spring Boot. If this cannot be the default, maybe the Spring Open Feign documentation could have a session explaining this setting.

Thanks for the help.

Best regards, Rafael Pacheco.

Comment From: poznachowski

It was working that way, but this commit changed that: https://github.com/spring-cloud/spring-cloud-openfeign/commit/505a83e6a4ff3500cb7230b0e8f0da9d7dc13e75#diff-75f79e40d74af2b7ff1cfb558b90206e

where the test falsely assume that FeignClientsConfiguration is available for the Application: https://github.com/spring-cloud/spring-cloud-openfeign/commit/505a83e6a4ff3500cb7230b0e8f0da9d7dc13e75#diff-a49839663a8c102988e6435a470f4679R89

Comment From: OlgaMaciaszek

Thanks @poznachowski - makes sense. Would you like to create a PR with a fix?

Comment From: ammmze

I can confirm this is an issue. I've set up a vanilla demo app that reproduces this issue.

https://github.com/ammmze/spring-cloud-openfeign-demo-no-page

Note: the above demo is spring boot 2.1 as that is where i'm attempting to use it, but i've also tried it in spring boot 2.2 with Hoxton.SR1 and it also fails there.

Comment From: ammmze

And while I found that the workaround posted here works in the above demo app, it did not work in my main application where there are a bunch of other hairy configurations as that app has evolved over time.

Edit: Figured out why it didn't work in my big hairy app...we had some custom ObjectMapper beans we were registering in there so spring wasn't creating it's normal one and injecting the modules. But again this only gets the workaround working. The vanilla functionality is still broken.

Comment From: ammmze

A brute force way to deal with this would be to register the module in the client configuration (can be done either in the @FeignClient or the @EnableFeignClient). Something like this:

@FeignClient(name = "strings", url = "http://localhost:${server.port:8080}/", configuration = ClientConfiguration.class)
public interface StringClient {
    @GetMapping("/api")
    Page<String> getStrings(Pageable pageable);

    class ClientConfiguration implements InitializingBean {
        private List<AbstractJackson2HttpMessageConverter> converters;
        private List<Module> modules;

        public ClientConfiguration(List<AbstractJackson2HttpMessageConverter> converters,
                List<Module> modules) {
            this.converters = converters;
            this.modules = modules;
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            converters.forEach(converter ->
                    modules.forEach(module -> converter.getObjectMapper().registerModule(module)));
        }
    }
}

Note: The above doesn't do any checking/comparison to see if the module has been registered already.

Comment From: ryandanielspmc

The problem was fixed for me after upgrading to org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR11.

Comment From: okue

It seems beaning SortJacksonModule in Spring's default ApplicationContext caused this error https://github.com/spring-cloud/spring-cloud-openfeign/issues/492.

If possible, it might be happy to bean SortJacksonModule only in separate Feign's ApplicationContext.

Comment From: myong3

According to this reference: https://stackoverflow.com/a/66135597 It work for me.

If you use autoconfigured Feign client you can follow Spring Cloud OpenFeign docs by turning on corresponding configuration property:

You may consider enabling Jackson Modules for the support org.springframework.data.domain.Page and org.springframework.data.domain.Sort decoding.

feign.autoconfiguration.jackson.enabled=true