RestClientException:无法提取响应。找不到合适的HttpMessageConverter

时间:2017-05-25 08:55:11

标签: json spring rest spring-mvc resttemplate

使用curl命令:

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

我收到了JSON回复:

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

我将响应保存在txt文件中并使用jackson解析它,一切都很好

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

我认为我应该使用RestTemplate得到相同的结果,但事实并非如此

RestTemplate restTemplate = new RestTemplate();
        MessageList messageList = 
                restTemplate.getForObject("http://592693f43c87815f9b8145e9:f099c85d84d4e325a2186c02bd0caeef@backend.tdk.com/api/devicetypes/591570373c87894b4eece34d/messages", MessageList.class);

我收到了错误

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)

我尝试设置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);

然后我收到了编译错误

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

我还试图添加一个Jackson Message转换器

  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);

但后来我收到了这个错误:

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)

我也尝试添加类

@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;
    }

}

但我得到了错误:

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)

11 个答案:

答案 0 :(得分:57)

这里的主要问题是从服务中收到的内容类型[text / html; charset = iso-8859-1] ,但实际内容类型应为 application / json; charset = iso-8859-1

为了克服这个问题,您可以引入自定义消息转换器。并为所有类型的响应注册它(即忽略响应内容类型标题)。就像这样

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); 

答案 1 :(得分:18)

我遇到了一个非常类似的问题,结果很简单;我的客户端不包括Jackson依赖项,即使所有编译的代码正确,但JSON的自动魔术转换器未被包含在内。见this RestTemplate-related solution.

简而言之,我在我的pom.xml中添加了一个Jackson依赖项,它只是起作用了:

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

答案 2 :(得分:6)

虽然接受的答案解决了OP的原始问题,但我怀疑大多数人通过Google搜索找到此问题的可能性(从统计角度而言)可能是一个完全不同的问题,恰好抛出相同的没有合适的HttpMessageConverter找到异常。

幕后发生的事情是MappingJackson2HttpMessageConverter吞噬了其canRead()方法中发生的任何异常,该方法应该能够自动检测有效载荷是否适合json解码。该异常被一个简单的布尔值返回所代替,该布尔值基本上传达了抱歉,我不知道如何将该消息解码到更高级别的API(RestClient)。只有在所有其他转换器的canRead()方法返回false之后,更高级别的API才会抛出找不到合适的HttpMessageConverter 异常,从而完全掩盖了真正的问题。

对于尚未找到根本原因的人(例如您和我,但不是OP),解决此问题的方法是在onMappingJackson2HttpMessageConverter.canRead()上放置调试器断点,然后在任意位置启用通用断点例外,然后点击继续。下一个例外是真正的根本原因。

我的具体错误恰巧是其中一个bean引用了缺少正确的反序列化注释的接口。

答案 3 :(得分:3)

如果 @Ilya Dyoshin 的上述回复仍无法检索, 尝试将响应转换为String对象。

(我自己认为错误是由Ilya的代码片段解决的,检索到的响应是来自服务器的失败(错误)。)

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); 

并转换为ResponseObject DTO(Json)

Gson g = new Gson();
DTO dto = g.fromJson(st.getBody(), DTO.class); 

答案 4 :(得分:2)

在我的情况下,@ Ilya Dyoshin的解决方案不起作用:不允许使用媒体类型“ *”。 我通过在MockRestServiceServer初始化期间以这种方式将新的转换器添加到restTemplate来解决此错误:

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

(基于Yashwant Chavan在名为Technicalkeeda的博客上提出的解决方案)

JN Gerbaux

答案 5 :(得分:1)

在我的情况下,这是由运行时类路径中缺少杰克逊核心,杰克逊注释和杰克逊数据绑定jar引起的。 它没有抱怨通常的ClassNothFoundException异常,而是最初问题中提到的错误。

答案 6 :(得分:1)

请添加具有 jackson databind 包的共享依赖项。希望这能解决问题。

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

答案 7 :(得分:1)

通过这种方式,您可以使用 resttemplate 获取对象响应并使用 MediaType.APPLICATION_JSON

设置 contentType
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 
  }

答案 8 :(得分:0)

其他可能的解决方案:我试图用私有类实例(在我的工作类内部定义)映射restTemplate.getForObject的结果。它不起作用,但是如果我在自己的文件中将对象定义为public,则它可以正常工作。

答案 9 :(得分:0)

当响应缺少该字段时,Spring会将默认内容类型设置为octet-stream。您所需要做的就是添加一个消息转换器来解决此问题。

答案 10 :(得分:0)

我尝试使用Feign时遇到了同样的问题,据我了解,HTTP消息转换器会有所帮助,但想了解如何实现。

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

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

您必须对解码器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);
        }
    }

}