Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RestClientException: Could not extract response. no suitable HttpMessageConverter found

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) 
like image 220
Nunyet de Can Calçada Avatar asked May 25 '17 08:05

Nunyet de Can Calçada


People also ask

What causes RestClientException?

Class RestClientException Base class for exceptions thrown by RestTemplate in case a request fails because of a server error response, as determined via ResponseErrorHandler. hasError(ClientHttpResponse) , failure to decode the response, or a low level I/O error.

What is REST client exception?

RestClientException(java.lang.String pMessage) A constructor which takes a message, response object and error code. RestClientException(java.lang.String pMessage, java.lang.Throwable pCause) A constructor which takes a source exception, response object and error code.


2 Answers

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);  
like image 54
Ilya Dyoshin Avatar answered Sep 18 '22 20:09

Ilya Dyoshin


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.

like image 23
Alex R Avatar answered Sep 22 '22 20:09

Alex R