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

Json RestClientException:无法提取响应。未找到合适的HttpMessageConverter,json,spring,rest,spring-mvc,resttemplate,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",... 我将响应保存在

使用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);
我还尝试添加了这个类

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

}

这里的主要问题是从服务接收到的内容类型[text/html;charset=iso-8859-1],但是真正的内容类型应该是application/json;字符集=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); 
List>();
//添加Jackson消息转换器
MappingJackson2HttpMessageConverter=新的MappingJackson2HttpMessageConverter();
//注意:这里我们制作这个转换器来处理任何类型的响应,
//不仅仅是application/*json,这是默认行为
setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restemplate.setMessageConverters(messageConverters);

如果@Ilya Dyoshin的上述响应仍然无法检索, 尝试将响应放入字符串对象中

(就我个人而言,Ilya的代码片段解决了这个错误,检索到的响应是来自服务器的失败(错误)


我遇到了一个非常类似的问题,结果很简单;我的客户机没有包含Jackson依赖项,即使代码编译正确,也没有包含JSON的自动魔术转换器。看

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

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

com.fasterxml.jackson.core
杰克逊数据绑定
2.5.1

在我的例子中@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

虽然公认的答案解决了OP的原始问题,但大多数通过谷歌搜索找到这个问题的人可能有一个完全不同的问题,只是碰巧抛出了相同的“没有合适的HttpMessageConverter发现异常”

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

对于尚未找到根本原因的人(如您和我,但不是OP),解决此问题的方法是在
onMappingJackson2HttpMessageConverter.canRead()
,然后在任何异常上启用常规断点,然后单击Continue。下一个例外是真正的根本原因

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

从未来更新

事实证明,在我的许多项目中,这是一个反复出现的问题,因此我开发了一个更主动的解决方案。每当我需要以独占方式处理JSON(无XML或其他格式),我现在就用以下实例替换我的
RestTemplate
bean:

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

    }
}
公共类JsonRestTemplate扩展RestTemplate{
公共JsonRestTemplate(
客户前传工厂客户前传工厂){
超级(clientHttpRequestFactory);
//强制使用一个合理的JSON映射器。
//根据项目“合理”定义的需要进行定制:
ObjectMapper ObjectMapper=新的ObjectMapper()
.registerModule(新的Jdk8Module())
.registerModule(新的JavaTimeModule())
.配置(
SerializationFeature.WRITE_DATES_作为_时间戳,false);

List在我的例子中,这是由于运行时类路径中缺少jackson核心、jackson注释和jackson数据绑定JAR造成的。
它没有像人们预期的那样抱怨通常的ClassNothFoundException,而是抱怨原始问题中提到的错误。

在发出GET请求之前,您需要创建自己的转换器并实现它

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);    
RestTemplate RestTemplate=new RestTemplate();
列表>();
MappingJackson2HttpMessageConverter=新的MappingJackson2HttpMessageConverter();
setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restemplate.setMessageConverters(messageConverters);

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

当响应缺少该字段时,Spring将默认内容类型设置为
八位字节流。
。您需要做的就是添加一个消息转换器来修复此问题。

每当我看到这种类型的错误时,我都会遵循以下方法:

  • 调试此问题的一种方法是,首先将任何响应作为String.class,然后应用
    Gson().fromJson(StringResp.body(),MyDTO.class)
    。 还是会的
    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); 
    
    Gson g = new Gson();
    DTO dto = g.fromJson(st.getBody(), DTO.class); 
    
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.5.1</version>
    </dependency>
    
      MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = 
                          new MappingJackson2HttpMessageConverter();
      mappingJackson2HttpMessageConverter.setSupportedMediaTypes(
                                        Arrays.asList(
                                           MediaType.APPLICATION_JSON, 
                                           MediaType.APPLICATION_OCTET_STREAM));
      restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
      mockServer = MockRestServiceServer.createServer(restTemplate);
    
    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);
    
        }
    }
    
    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);    
    
    MyDTO resp = g.fromJson(respStr.getBody(), MyDTO.class);
    
    @FeignClient(name = "mobilesearch", url = "${mobile.search.uri}" ,
            fallbackFactory = MobileSearchFallbackFactory.class,
            configuration = MobileSearchFeignConfig.class)
    public interface MobileSearchClient {
    
        @RequestMapping(method = RequestMethod.GET)
        List<MobileSearchResponse> getPhones();
    }
    
    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);
            }
        }
    
    }
    
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.12.1</version>
      </dependency>