RestClientException: Could not extract response. no suitable HttpMessageConverter found
Asked Answered
G

16

94

Using the curl command:

curl -u 591bf65f50057469f10b5fd9:0cf17f9b03d056ds0e11e48497e506a2 https://backend.tdk.com/api/devicetypes/59147fd79e93s12e61499ffe/messages

I am getting a JSON response:

{"data":[{"device":"18SE62","time":1494516023,"data":"3235","snr":"36.72",...

I save the response on a txt file and parse it using jackson, and everything is fine

ObjectMapper mapper = new ObjectMapper();
        File f = new File(getClass().getResource
                    ("/result.json").getFile());
        MessageList messageList = mapper.readValue(f, MessageList.class);

and I assume I should get the same result using RestTemplate but that's not the case

RestTemplate restTemplate = new RestTemplate();
        MessageList messageList = 
                restTemplate.getForObject("http://592693f43c87815f9b8145e9:[email protected]/api/devicetypes/591570373c87894b4eece34d/messages", MessageList.class);

I got an error instead

Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
    at com.tdk.controllers.restful.client.RestTemplateExample.main(RestTemplateExample.java:27)

I tried to set the contentType:

HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);


        MessageList messageList = 
                restTemplate.getForObject(url, entity, MessageList.class);

but then I got a compilation error

The method getForObject(String, Class<T>, Object...) in the type RestTemplate is not applicable for the arguments (String, HttpEntity<String>, 
 Class<MessageList>)

I also tried to add a the Jackson Message converter

  List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();        
            //Add the Jackson Message converter
            messageConverters.add(new MappingJackson2HttpMessageConverter());    
            //Add the message converters to the restTemplate
            restTemplate.setMessageConverters(messageConverters); 

            MessageList messageList = 
                    restTemplate.getForObject(url, MessageList.class);

But then I got this error:

Exception in thread "main" org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
    at com.tdk.controllers.restful.client.RestTemplateExample.main(RestTemplateExample.java:51)

I also tried adding the class

@Configuration
@EnableWebMvc
public class MvcConf extends WebMvcConfigurationSupport {

    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(converter());
        addDefaultHttpMessageConverters(converters);
    }

    @Bean
    MappingJackson2HttpMessageConverter converter() {

        MappingJackson2HttpMessageConverter converter 
                    = new MappingJackson2HttpMessageConverter();
        return converter;
    }

}

but I got the error:

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.tdk.domain.backend.MessageList] and content type [text/html;charset=iso-8859-1]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
Gramnegative answered 25/5, 2017 at 8:55 Comment(5)
What if you specify content type (see #38286472)Fulfill
Can you show your MessageList class ? your Controller ?Puli
Are you sure the response header contain the response content type is json? Because the message says the content is text/html;charset..Splanchnic
no, I am not sure. I have not develop the RESTful Web service :-(Nat
private HttpEntity<MessageList> connectToRestService() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://592693f43c87815f9b8145e9:[email protected]/api/devicetypes/591570373c87894b4eece34d/messages"); HttpEntity<MessageList> entity = new HttpEntity<>(headers); HttpEntity<MessageList> response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, entity, MessageList.class); return response; } try this?Plated
M
169

The main problem here is content type [text/html;charset=iso-8859-1] received from the service, however the real content type should be application/json;charset=iso-8859-1

In order to overcome this you can introduce custom message converter. and register it for all kind of responses (i.e. ignore the response content type header). Just like this

List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();        
//Add the Jackson Message converter
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();

// Note: here we are making this converter to process any kind of response, 
// not only application/*json, which is the default behaviour
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));        
messageConverters.add(converter);  
restTemplate.setMessageConverters(messageConverters); 
Metaphrast answered 27/5, 2017 at 17:33 Comment(6)
Thanks u saved my day . I had same issue where service was returning xml but content type was wrong as OctetFond
This answer is not a universal solution... I have application/json; utf-8 coming from the service and properly registered as the message converter for Jackson, and it's not working.Awesome
I would convert the line to converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));Unexpected
I'm getting java.lang.IllegalArgumentException: 'Content-Type' cannot contain wildcard type '*'Mcglynn
@user7294900 Then please try to use a specific MediaType like MediaType.APPLICATION_PDF instead of MediaType.ALLWharfinger
perhaps the only reason above solution did not work for me when consuming html response in spring template was in failing to add below code to the header Charset utf8 = Charset.forName("UTF-8"); MediaType mediaType = new MediaType("text", "html", utf8); headers.setContentType(mediaType); when calling ResponseEntity<String> objectResponseEntity = restTemplate.exchange(ApiUrl, HttpMethod.POST, httpEntity, String.class);Sweptback
A
76

While the accepted answer solved the OP's original problem, most people finding this question through a Google search are likely having an entirely different problem which just happens to throw the same no suitable HttpMessageConverter found exception.

What happens under the covers is that MappingJackson2HttpMessageConverter swallows any exceptions that occur in its canRead() method, which is supposed to auto-detect whether the payload is suitable for json decoding. The exception is replaced by a simple boolean return that basically communicates sorry, I don't know how to decode this message to the higher level APIs (RestClient). Only after all other converters' canRead() methods return false, the no suitable HttpMessageConverter found exception is thrown by the higher-level API, totally obscuring the true problem.

For people who have not found the root cause (like you and me, but not the OP), the way to troubleshoot this problem is to place a debugger breakpoint on onMappingJackson2HttpMessageConverter.canRead(), then enable a general breakpoint on any exception, and hit Continue. The next exception is the true root cause.

My specific error happened to be that one of the beans referenced an interface that was missing the proper deserialization annotations.

UPDATE FROM THE FUTURE

This has proven to be such a recurring issue across so many of my projects, that I've developed a more proactive solution. Whenever I have a need to process JSON exclusively (no XML or other formats), I now replace my RestTemplate bean with an instance of the following:

public class JsonRestTemplate extends RestTemplate {

    public JsonRestTemplate(
            ClientHttpRequestFactory clientHttpRequestFactory) {
        super(clientHttpRequestFactory);

        // Force a sensible JSON mapper.
        // Customize as needed for your project's definition of "sensible":
        ObjectMapper objectMapper = new ObjectMapper()
                .registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule())
                .configure(
                        SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter() {

            public boolean canRead(java.lang.Class<?> clazz,
                    org.springframework.http.MediaType mediaType) {
                return true;
            }    
            public boolean canRead(java.lang.reflect.Type type,
                    java.lang.Class<?> contextClass,
                    org.springframework.http.MediaType mediaType) {
                return true;
            }
            protected boolean canRead(
                    org.springframework.http.MediaType mediaType) {
                return true;
            }
        };

        jsonMessageConverter.setObjectMapper(objectMapper);
        messageConverters.add(jsonMessageConverter);
        super.setMessageConverters(messageConverters);

    }
}

This customization makes the RestClient incapable of understanding anything other than JSON. The upside is that any error messages that may occur will be much more explicit about what's wrong.

Awesome answered 30/8, 2018 at 18:12 Comment(4)
This was a valuable insight.Highwrought
Since I was using a TestRestTemplate I used a String result type instead of the bean, since Spring will use the string converter you will get at least a more meaningful message (for me it was a 400 Bad Request).Sagesagebrush
This needs more upvotes. To find out which is the problematic exception the breakpoint switching between AbstractJackson2HttpMessageConverter.canRead() and Exception [include Subclasses] (caught and uncaught) had to happen multiple times for me. Eventually I was lead to an IllegalArgumentException("Conflicting getter definitions for property <x> [...])" so I had to @JsonIgnore the incorrect one to deserialize.Armillas
For those like me, interested in serializing an object to send to some servers, you need to debug the canWrite() method instead. On my side, I found the real underlying exception in com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.hasSerializerFor(). Try to put some breakpoints there.Queensland
N
53

I was having a very similar problem, and it turned out to be quite simple; my client wasn't including a Jackson dependency, even though the code all compiled correctly, the auto-magic converters for JSON weren't being included. See this RestTemplate-related solution.

In short, I added a Jackson dependency to my pom.xml and it just worked:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.1</version>
</dependency>
Newburg answered 8/2, 2018 at 20:59 Comment(1)
In the article referred, the content-type is already set to application/json. The original post is for content-type text/htmlUnzip
S
15

One way to debug this issue is to first take the response as a String.class and then use

Gson().fromJson(StringResp.body(), MyDTO.class)

It will most likely still fail, but this time it will throw the fields that caused the error in the first place. Following the modification, we can resume using the previous approach as before.

ResponseEntity<String> respStr = restTemplate.exchange(URL,HttpMethod.GET, entity,  String.class);  
Gson g = new Gson();
    

The following step will generate an error with the fields that are causing the problem.

MyDTO resp = g.fromJson(respStr.getBody(), MyDTO.class);

I don't have the error message with me, but it will point to the problematic field and explain why. Resolve those and try the previous approach again.

Sibyls answered 16/1, 2020 at 23:47 Comment(2)
Thanks, this approach helped me. In my case my backend CRM system is returning an actual html instead of json. After taking the response as a string and not as the actual object I was expecting, I am able to see an error being thrown by CRM in the form of html.Fourposter
This approach was helpful because the problem was with the class I was trying to deserializePenelopepeneplain
F
10

If the above response by @Ilya Dyoshin didn't still retrieve, try to get the response into a String Object.

(For my self thought the error got solved by the code snippet by Ilya, the response retrieved was a failure(error) from the server.)

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
ResponseEntity<String> st = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class); 

And Cast to the ResponseObject DTO (Json)

Gson g = new Gson();
DTO dto = g.fromJson(st.getBody(), DTO.class); 
Forworn answered 12/9, 2017 at 6:21 Comment(2)
You can also set the content type this way. requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);Birdwell
I had a similar problem, used this solution and then the regular ObjectMapper to get my object back. A snippet: HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, "text/json"); ResponseEntity<String> resp = restTemplate.exchange(configs.getAddress(), HttpMethod.GET, new HttpEntity(headers), String.class); Jwks jwks = new ObjectMapper().readValue(resp.getBody(), Jwks.class);Godwit
A
4

In my case @Ilya Dyoshin's solution didn't work: The mediatype "*" was not allowed. I fix this error by adding a new converter to the restTemplate this way during initialization of the MockRestServiceServer:

  MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = 
                      new MappingJackson2HttpMessageConverter();
  mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
                                    Arrays.asList(
                                       MediaType.APPLICATION_JSON, 
                                       MediaType.APPLICATION_OCTET_STREAM));
  restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
  mockServer = MockRestServiceServer.createServer(restTemplate);

(Based on the solution proposed by Yashwant Chavan on the blog named technicalkeeda)

JN Gerbaux

Alta answered 7/8, 2018 at 14:3 Comment(2)
That did it for me too.Fuchs
Where do you set it in spring-boot?Vagal
G
3

You need to create your own converter and implement it before making a GET request.

RestTemplate  restTemplate = new RestTemplate();

List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();        

MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));         
messageConverters.add(converter);  
restTemplate.setMessageConverters(messageConverters);    
Gilleod answered 12/3, 2019 at 7:31 Comment(0)
T
3

Please add the shared dependency having jackson databind package . Hope this will clear the issue.

  <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.1</version>
  </dependency>
Twyla answered 17/2, 2021 at 14:25 Comment(1)
Thanks , that solved my problem I was having error: Restclientexception no httpmessageconverter I would like to mention that adding the "spring-boot-starter-web" also solved my problem , but when I tried the jackson dependency I like this more. It's simply more preciseJahn
C
3

This way you can get the List of Object response (List of Employee object) using resttemplate and set contentType using MediaType.APPLICATION_JSON

public List<Employee> getListofEmployee()
 {
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    HttpEntity<String> entity = new HttpEntity<String>(headers);
    ResponseEntity<List<Employee>> response = restTemplate.exchange("http://hello-server/rest/employees",
    HttpMethod.GET,entity, new ParameterizedTypeReference<List<Employee>>() {});
    return response.getBody(); //this returns List of Employee 
  }

if you are using jackson library, then don't forget to add it in pom.xml

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.14.1</version>
</dependency>
Comnenus answered 19/7, 2021 at 14:51 Comment(1)
add a simple header here solved my problemStollings
C
2

Spring sets the default content-type to octet-stream when the response is missing that field. All you need to do is to add a message converter to fix this.

Capitate answered 5/12, 2019 at 7:53 Comment(0)
H
1

In my case it was caused by the absence of the jackson-core, jackson-annotations and jackson-databind jars from the runtime classpath. It did not complain with the usual ClassNothFoundException as one would expect but rather with the error mentioned in the original question.

Homestretch answered 27/11, 2018 at 9:36 Comment(0)
P
0

Other possible solution : I tried to map the result of a restTemplate.getForObject with a private class instance (defined inside of my working class). It did not work, but if I define the object to public, inside its own file, it worked correctly.

Pimbley answered 9/8, 2019 at 13:7 Comment(0)
Y
0

I was trying to use Feign, while I encounter same issue, As I understood HTTP message converter will help but wanted to understand how to achieve this.

@FeignClient(name = "mobilesearch", url = "${mobile.search.uri}" ,
        fallbackFactory = MobileSearchFallbackFactory.class,
        configuration = MobileSearchFeignConfig.class)
public interface MobileSearchClient {

    @RequestMapping(method = RequestMethod.GET)
    List<MobileSearchResponse> getPhones();
}

You have to use Customer Configuration for the decoder, MobileSearchFeignConfig,

public class MobileSearchFeignConfig {

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


    @Bean
    public Decoder feignDecoder() {
        return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
    }

    public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new MappingJackson2HttpMessageConverter());
        return new ObjectFactory<HttpMessageConverters>() {
            @Override
            public HttpMessageConverters getObject() throws BeansException {
                return httpMessageConverters;
            }
        };
    }

    public class MappingJackson2HttpMessageConverter extends org.springframework.http.converter.json.MappingJackson2HttpMessageConverter {
        MappingJackson2HttpMessageConverter() {
            List<MediaType> mediaTypes = new ArrayList<>();
            mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
            setSupportedMediaTypes(mediaTypes);
        }
    }

}
Yearbook answered 12/8, 2020 at 8:44 Comment(0)
S
0

In my case i was missing the No Args contructor

@Data
@AllArgsConstructor
@NoArgsConstructor

for those who are no using Lombok do add no args constructor in the mapping pojo

public ClassA() {
        super();
        // TODO Auto-generated constructor stub
    }

also dont forget to add the Bean of Restemplate in main file if you are using the same

Scotsman answered 6/8, 2022 at 12:8 Comment(0)
W
0

Infuriating problem right? You just wanna get the result of the call, and you have a deSerialization error...that you have no clue where to look for. Well, all is not lost. If you change the type of call to String, you can get the JSON equivalent and then write a test to see why it is not serializing:

RestTemplate restTemplate = new RestTemplate();
String messageListString = restTemplate.getForObject("http://592693f43c87815f9b8145e9:[email protected]/api/devicetypes/591570373c87894b4eece34d/messages", String.class);

Here is an example with an input param I used in my Kotlin project:

fun givenCUT_whenFetchingBillableItemsForAPastMonthWithoutBillingData_thenWeSucceedInGettingAnEmptyXmlResponse() {
        val restTemplate = RestTemplate()
        val uri = "http://localhost:$port/api/test/billing/xml/month/{month}/"
        val params: MutableMap<String, String> = HashMap()
        params["month"] = "2022-09-01"
        val stringResponse = restTemplate.getForObject(uri, String::class.java, params)
        assertNotNull(stringResponse)
        assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
                "<bi:billableItems xmlns:bi=\"urn:blahblahblah\"/>\n", stringResponse)
} 

If you step through that test, and harvest the actual JSON of your endpoint, you can then use a test like this, to pump it in, and see why Jackson or Gson is complains:

@Test
fun givenCUT_whenDeSerializingBEStateCorrectionsResponse_thenWeGetAnInstanceOfAListOfBillingOrdersSuccessfully() {
        //Raw JSON harvested from BillingOrderControllerTest
        val harvestedFEJsonBillingOrderList = "YOUR JSON Harvested from above goes here" 
        val mapper = ObjectMapper()
        mapper.registerModule(JavaTimeModule())
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        val deSerBillingOrderList = mapper.readValue(harvestedFEJsonBillingOrderList, Array<BillingOrder>::class.java)
        assertNotNull(deSerBillingOrderList)
        assertEquals(1, deSerBillingOrderList.size)
}

The post is just as easy...the following is a snippet but you can see I finally gave up and commented out the serialization error part, and reverted to the String version and did the necessary with the test above in Jackson; I just this minute did that, and found 4 issues in the de-serialized JSON that Jackson explicitly reported on, and that I will fix. Then, I will revert the below to the typed version and it should have solved the problem:

 try {
            val result = restTemplate!!.postForEntity(uri, billingOrders, String::class.java)
           /* val result = restTemplate!!.postForObject(
                baseUrl,
                billingOrders,
                ResponseEntity<List<BillingOrder>>::class.java)*/
            assertNotNull(result)
        } catch (e: Exception) {
            log.error("Failed restTemplate.postForObject with $e")
        }
Wongawonga answered 28/11, 2022 at 16:1 Comment(0)
S
0

I understand there are lots of answers and some repetitive. Perhaps the solution of llya dyoshin will work for most.

I am providing an entire solution here adopting pieces from user9709261 and Knight1128, for spring rest template from which I was able to consume an html response and get the status 200.

HttpHeaders headers = new HttpHeaders();
Charset utf8 = Charset.forName("UTF-8");
MediaType mediaType = new MediaType("text", "html", utf8);
headers.setContentType(mediaType);
HttpEntity<Object> httpEntity = new HttpEntity<>(postResponse, headers);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(webhookURL);
ApiUrl = new URI(builder.buildAndExpand(urlParams).toUri().toString());

MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_HTML));
restTemplate.getMessageConverters().add(converter);

ResponseEntity<String> objectResponseEntity = restTemplate.exchange(ApiUrl, HttpMethod.POST, httpEntity, String.class);

Object body = objectResponseEntity.getBody();
return objectResponseEntity.getStatusCodeValue();




    
Sweptback answered 26/6, 2023 at 14:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.