urig
urig

Reputation: 16831

Spring Feign: Could not extract response: no suitable HttpMessageConverter found for response type

I am trying to get a Spring Cloud Netflix Feign client to fetch a bit of JSON over HTTP and convert it to an object. I keep getting this error instead:

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class io.urig.checkout.Book] and content type [application/json;charset=UTF-8]

Here's the bit of JSON returned from the remote service:

{
    "id": 1,
    "title": "Moby Dick",
    "author": "Herman Melville"
}

Here's the corresponding class I'm trying to deserialize to:

package io.urig.checkout;

public class Book {
    private long id;
    private String title;
    private String author;

    public Book() {}

    public Book(long id, String title, String author) {
        super();
        this.id = id;
        this.title = title;
        this.author = author;
    }

    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}

And here's my Feign client:

package io.urig.checkout;

import java.util.Optional;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import io.urig.checkout.Book;

@FeignClient(name="inventory", url="http://localhost:8080/")
public interface InventoryClient {

    @RequestMapping(method = RequestMethod.GET, value = "books/{bookId}")
    public Optional<Book> getBookById(@PathVariable(value="bookId") Long bookId);

}

What do I need to do to get this to work?

Upvotes: 8

Views: 33973

Answers (7)

cellepo
cellepo

Reputation: 4479

I got the same sort of error from a @PostMapping in my FeignClient, as a side-effect from an error in the PostMapping Annotation's path param. It related to my FeignClient referring to a properties file, for its reference there of the value for the path param:

@PostMapping(path = "{my.property-name-from-properties-file}")

The property reference for path wasn't resolving correctly, and once I solved that, it solved the feign.codec.DecodeException in Question here. (and was troubleshooted first, by giving the path value a hard-coded String, instead of the property value reference).

In my case, I had forgotten the $ char needed to be prepended:

@PostMapping(path = "${my.property-name-from-properties-file}")

Upvotes: 0

Toji
Toji

Reputation: 250

I am late here but I would like to add one more point. In my case I observed Spring Feign client returns this exception when you specified the return type as a specific model/entity class and that entity is not found.

You should check the response for the another service which you are calling and see what response it returns in case the entity is not found, or in case an exception is thrown.

So in case an entity is not found or any exception is thrown and that response does not match to what you have specified in return type then this exception is thrown in the client service.

Upvotes: 0

Konstantin
Konstantin

Reputation: 73

Sorry, for too late answer.

Had the same problem.

Just add two parameters to your @RequestMapping -

consumes = "application/json", produces = "application/json"

In your code this will look like this -

package io.urig.checkout;

import java.util.Optional;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import io.urig.checkout.Book;

@FeignClient(name="inventory", url="http://localhost:8080/")
public interface InventoryClient {

@RequestMapping(method = RequestMethod.GET, value = "books/{bookId}", consumes = "application/json", produces = "application/json")
public Optional<Book> getBookById(@PathVariable(value="bookId") Long bookId);

}

Upvotes: 1

urig
urig

Reputation: 16831

Thanks to all who tried to help!

As it turned out my issue was a defective Maven dependency, probably corrupted during download or installation. Having entirely deleted the .m2/repository folder on my machine and then updating Maven dependencies for the project the issue is now gone.

Upvotes: 1

Kevin Davis
Kevin Davis

Reputation: 1293

You will need to ensure that you have at least one JSON library on your classpath. Feign supports both GSON and Jackson and Spring Cloud OpenFeign will autoconfigure the SpringEncoder and SpringDecoder instances with the appropriate MessageConverter if they are found on your classpath. Ensure that you have at least one of the following in your pom.xml or build.gradle

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.4</version>
</dependency>

or

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
</dependency>

Once they are found, Spring will register the appropriate MessageConverter

Upvotes: 3

RBH
RBH

Reputation: 271

I don't know Feign, but when I've had "no suitable HttpMessageConverter found..." errors in the past, it's because the content type has not been registered. Perhaps you need to add this to the RequestMapping:

consumes = "application/json"

All I can suggest is to try to confirm if Feign configuration has MappingJackson2HttpMessageConverter registered as a converter for Book. Not sure if this is something that should work out of the box with Feign, or if you have to do it manually. I see an example on Feign's GitHub that has:

GitHub github = Feign.builder()
                 .encoder(new JacksonEncoder())
                 .decoder(new JacksonDecoder())
                 .target(GitHub.class, "https://api.github.com");

Have you created configuration using Feign.builder() or some equivalent configuration files?

Upvotes: 5

FDirlikli
FDirlikli

Reputation: 195

I think your problem is the response type. Try converting it to Book from Optional. If you want to return an Optional than you should provide your custom converter.

Upvotes: 2

Related Questions