【问题标题】:RestClientException: Could not extract response. no suitable HttpMessageConverter foundRestClientException:无法提取响应。找不到合适的 HttpMessageConverter
【发布时间】:2017-10-25 20:35:16
【问题描述】:

使用 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)

【问题讨论】:

  • 如果指定内容类型会怎样(参见stackoverflow.com/questions/38285972/…
  • 你能展示你的 MessageList 类吗?你的控制器?
  • 你确定响应头包含响应内容类型是json吗?因为消息说内容是 text/html;charset..
  • 不,我不确定。我还没有开发 RESTful Web 服务 :-(
  • private HttpEntity&lt;MessageList&gt; connectToRestService() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://592693f43c87815f9b8145e9:f099c85d84d4e325a2186c02bd0caeef@backend.tdk.com/api/devicetypes/591570373c87894b4eece34d/messages"); HttpEntity&lt;MessageList&gt; entity = new HttpEntity&lt;&gt;(headers); HttpEntity&lt;MessageList&gt; response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, entity, MessageList.class); return response; } 试试这个?

标签: json spring rest spring-mvc resttemplate


【解决方案1】:

这里的主要问题是从服务接收的内容类型 [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); 

【讨论】:

  • 谢谢你拯救了我的一天。我有同样的问题,服务返回 xml,但内容类型错误为 Octet
  • 这个答案不是一个通用的解决方案...我有 application/json;来自服务的 utf-8 并正确注册为 Jackson 的消息转换器,但它无法正常工作。
  • 我会将该行转换为converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
  • 我收到java.lang.IllegalArgumentException: 'Content-Type' cannot contain wildcard type '*'
  • @user7294900 然后请尝试使用特定的 MediaType,例如 MediaType.APPLICATION_PDF 而不是 MediaType.ALL
【解决方案2】:

虽然接受的答案解决了 OP 的原始问题,但大多数通过 Google 搜索找到此问题的人可能遇到了完全不同的问题,恰好抛出相同的找不到合适的 HttpMessageConverter 异常。

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

对于没有找到根本原因的人(像你我一样,但不是OP),解决这个问题的方法是在onMappingJackson2HttpMessageConverter.canRead()上放置一个调试器断点,然后在任何异常上启用一个通用断点,并点击继续。下一个例外是真正的根本原因。

我的具体错误恰好是其中一个 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);

    }
}

这种自定义使RestClient 无法理解 JSON 以外的任何内容。好处是可能出现的任何错误消息都会更明确地说明问题所在。

【讨论】:

  • 这是一个很有价值的见解。
  • 因为我使用的是TestRestTemplate,所以我使用了 String 结果类型而不是 bean,因为 Spring 将使用字符串转换器,您至少会收到一条更有意义的消息(对我来说是 400错误请求)。
  • 这需要更多的支持。为了找出有问题的异常,AbstractJackson2HttpMessageConverter.canRead()Exception [include Subclasses] (caught and uncaught) 之间的断点切换对我来说必须发生多次。最终我被引导到IllegalArgumentException("Conflicting getter definitions for property &lt;x&gt; [...])",所以我不得不@JsonIgnore 反序列化不正确的。
  • 对于像我这样对序列化对象以发送到某些服务器感兴趣的人,您需要调试 canWrite() 方法。在我这边,我在 com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.hasSerializerFor() 中发现了真正的底层异常。尝试在那里放置一些断点。
【解决方案3】:

我遇到了一个非常相似的问题,结果很简单;我的客户端没有包含 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>

【讨论】:

  • 在引用的文章中,content-type已经设置为application/json。原帖是针对内容类型的 text/html
【解决方案4】:

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

  1. 调试此问题的一种方法是首先将任何响应作为 String.class,然后应用 Gson().fromJson(StringResp.body(), MyDTO.class)。 它很可能仍然会失败,但这次它会首先抛出导致此错误的字段。修改后,我们可以照常使用之前的方法。

ResponseEntity&lt;String&gt; respStr = restTemplate.exchange(URL,HttpMethod.GET, entity, String.class);
Gson g = new Gson();

以下步骤将引发导致问题的字段错误

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

我没有收到错误消息,但它会指出存在问题的字段及其原因。解决这些问题,然后使用以前的方法重试。

【讨论】:

  • 谢谢,这种方法帮助了我。就我而言,我的后端 CRM 系统返回的是实际的 html 而不是 json。将响应作为字符串而不是我期望的实际对象后,我可以看到 CRM 以 html 的形式抛出错误。
【解决方案5】:

如果 @Ilya Dyoshin 的上述回复仍未检索到, 尝试将响应转换为字符串对象。

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

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

【讨论】:

  • 你也可以这样设置内容类型。 requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
  • 我遇到了类似的问题,使用了这个解决方案,然后使用了常规的 ObjectMapper 来取回我的对象​​。一个sn-p:HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, "text/json"); ResponseEntity&lt;String&gt; resp = restTemplate.exchange(configs.getAddress(), HttpMethod.GET, new HttpEntity(headers), String.class); Jwks jwks = new ObjectMapper().readValue(resp.getBody(), Jwks.class);
【解决方案6】:

在我的情况下,@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

【讨论】:

  • 我也这样做了。
  • 你在spring-boot的哪里设置的?
【解决方案7】:

在发出 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);    

【讨论】:

    【解决方案8】:

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

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

    【讨论】:

    • 谢谢,这解决了我的问题我有错误:Restclientexception no httpmessageconverter 我想提一下,添加“spring-boot-starter-web”也解决了我的问题,但是当我尝试杰克逊依赖我更喜欢这个。它只是更精确
    【解决方案9】:

    在我的情况下,这是由于运行时类路径中缺少 jackson-core、jackson-annotations 和 jackson-databind jar 引起的。 它并没有像人们期望的那样抱怨通常的 ClassNothFoundException,而是抱怨原始问题中提到的错误。

    【讨论】:

      【解决方案10】:

      这样您可以使用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 
        }
      

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

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

          【讨论】:

            【解决方案13】:

            我尝试使用 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);
                    }
                }
            
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-09-06
              • 1970-01-01
              • 1970-01-01
              • 2014-09-03
              • 2018-08-18
              相关资源
              最近更新 更多