Ticker
Ticker

Reputation: 141

How to decode JSon response with custom Feign client?

In my application, I have to know, from a list a server addresses, which are up. The solution I found is to call health endpoint from Spring-Boot Actuator, for each of them. The JSon reponse is:

{
    "status": "UP"
}

In other parts of the application, I use Feign clients from Spring-Cloud defined with the @FeignClient annotation, which works perfectly:

    @FeignClient(
            name = "tokenProxy",
            url = "${host}:${port}"
    )

Unfortunately this kind of configuration doesn't allow to re-use the same client to call the same endpoint on different addresses. So I have to define my own custom client (If there is another solution, do not hesitate to tell me! ):

    @GetMapping(
      value = "/servers"
    )
    public Server discover() {
      MyClient myClient = Feign.builder()
        .target(
          Target.EmptyTarget.create(
            MyClient.class
          )
        );

      return myClient.internalPing(URI.create("http://localhost:8090"));
    }

    interface MyClient {
        @RequestLine("GET /actuator/health")
        Server internalPing(URI baseUrl);
    }

    class Server {
        private final String status;

        @JsonCreator
        public Server(@JsonProperty("status") String status) {
            this.status = status;
        }

        public String getStatus() {
            return status;
        }
    }

When I call the endpoint /servers, I get the following error, indicating that my custom Feign client isn't confgured with the appropriate decoder:

feign.codec.DecodeException: class com.xxx.web.Server is not a type supported by this decoder.
    at feign.codec.StringDecoder.decode(StringDecoder.java:34) ~[feign-core-10.10.1.jar:na]
    at feign.codec.Decoder$Default.decode(Decoder.java:92) ~[feign-core-10.10.1.jar:na]
    at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:115) ~[feign-core-10.10.1.jar:na]
    at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:87) ~[feign-core-10.10.1.jar:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-10.10.1.jar:na]

I guess i should use JacksonDecoder, but I cannot find it in my dependencies from Spring-Cloud Hoxton.SR5:

      <dependencies>
    ...
        <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    ...
      </dependencies>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR5</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
  </dependencyManagement>

Could someone help me, either with a better solution for my need or a explanation on how to properly configure custom Feign clients?

Thanks in advance

Upvotes: 6

Views: 22963

Answers (3)

Bacar Pereira
Bacar Pereira

Reputation: 1145

I had to add the dependency below

      <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-jackson</artifactId>
        <version>12.0</version> <!-- Use the latest version available -->
    </dependency>

Next I had to make adjustments to feign.builder

MyClient cl = Feign.builder()
            .encoder(new JacksonEncoder())
            .decoder(new JacksonDecoder())
            .target(MyClient.class, urlBaseOrServiceName);

Upvotes: 2

Ticker
Ticker

Reputation: 141

Another way could be annotating the class with @Import(FeignClientsConfiguration.class) which is the default configuration provided by Spring Cloud Netflix.

Then it becomes easy to inject both Encoder and Decoder when creating the Feign client:

    @Import(FeignClientsConfiguration.class)
    @Configuration
    public class MyConfiguration {
    (...)
    Myclient myClient (Decoder feignDecoder, Encoder feignEncoder) {
    return Feign.builder()
        .decoder( feignDecoder )
        .encoder( feignEncoder )
        .target(
          Target.EmptyTarget.create(
            MyClient.class
          )
        );
    }

There are two different defined encoders in the configuration class (pageable or not), so pay attention to clearly identify which you want, either by its name or a qualifier.

Upvotes: 3

Ticker
Ticker

Reputation: 141

In fact, the library which includes Jackson decoder and encoder was not loaded by default when using spring-cloud dependencies. To fix the issue I simply had to add the following to my pom.xml file:

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-jackson</artifactId>
</dependency>

Upvotes: 5

Related Questions