没有找到适合响应类型的 HttpMessageConverter

Posted

技术标签:

【中文标题】没有找到适合响应类型的 HttpMessageConverter【英文标题】:no suitable HttpMessageConverter found for response type 【发布时间】:2014-03-18 05:48:10 【问题描述】:

使用弹簧,使用此代码:

List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter httpMessageConverter : messageConverters)
  System.out.println(httpMessageConverter);

ResponseEntity<ProductList> productList = restTemplate.getForEntity(productDataUrl,ProductList.class);

我明白了

org.springframework.http.converter.ByteArrayHttpMessageConverter@34649ee4
org.springframework.http.converter.StringHttpMessageConverter@39fba59b
org.springframework.http.converter.ResourceHttpMessageConverter@383580da
org.springframework.http.converter.xml.SourceHttpMessageConverter@409e850a
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@673074aa
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@1e3b79d3
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@52bb1b26

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.mycopmany.ProductList] and content type [text/html;charset=UTF-8]

pojo的sn-p:

@XmlRootElement(name="TheProductList")
public class ProductList 

@XmlElement(required = true, name = "date")
private LocalDate importDate;

【问题讨论】:

返回的媒体类型是text/html,而不是application/xml。您是否查看了响应以查看您是否正在获取错误消息的 HTML 页面,而不是您正在寻找的真实 XML 响应? @BlaiseDoughan 谢谢。没有它的数据,标题不正确。来自第三方... 所以我正在下载对文件的响应,转换为流源,并以这种方式使用解组器。 【参考方案1】:

从 Spring 的角度来看,使用 RestTemplate 注册的 HttpMessageConverter 实例都不能将 text/html 内容转换为 ProductList 对象。感兴趣的方法是HttpMessageConverter#canRead(Class, MediaType)。以上所有的实现都返回false,包括Jaxb2RootElementHttpMessageConverter

由于没有HttpMessageConverter 可以读取您的 HTTP 响应,因此处理失败并出现异常。

如果您可以控制服务器响应,请将其修改为将Content-type 设置为application/xmltext/xml 或与application/*+xml 匹配的值。

如果您不控制服务器响应,则需要编写和注册自己的HttpMessageConverter(可以扩展 Spring 类,参见AbstractXmlHttpMessageConverter 及其子类),它可以读取和转换@987654335 @。

【讨论】:

【参考方案2】:

您也可以简单地告诉您的RestTemplate 接受所有媒体类型:

@Bean
public RestTemplate restTemplate() 
   final RestTemplate restTemplate = new RestTemplate();

   List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
   MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
   converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
   messageConverters.add(converter);
   restTemplate.setMessageConverters(messageConverters);

   return restTemplate;

【讨论】:

不适用于响应文件并为我键入 application/actet-stream:HttpMessageNotReadableException:JSON 解析错误:无法从 START_ARRAY 令牌中反序列化 java.io.File 的实例【参考方案3】:

如果您使用的是 Spring Boot,您可能需要确保您的类路径中有 Jackson 依赖项。您可以通过以下方式手动执行此操作:

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

或者您可以使用网络启动器:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

【讨论】:

【参考方案4】:

如果您无法更改服务器媒体类型响应,您可以扩展 GsonHttpMessageConverter 以处理其他支持类型

public class MyGsonHttpMessageConverter extends GsonHttpMessageConverter 
    public MyGsonHttpMessageConverter() 
        List<MediaType> types = Arrays.asList(
                new MediaType("text", "html", DEFAULT_CHARSET),
                new MediaType("application", "json", DEFAULT_CHARSET),
                new MediaType("application", "*+json", DEFAULT_CHARSET)
        );
        super.setSupportedMediaTypes(types);
    

【讨论】:

除了自定义类有没有办法单独设置媒体类型? 一个改进只是使用现有的 GsonHttpMessageConverter 类,但调用 setSupportedMediaTypes() 设置器。【参考方案5】:

除了所有答案之外,如果您在预期其他内容(即application/json)时碰巧收到回复text/html,则可能表明服务器端发生错误(例如404)并且返回的是错误页面而不是您的数据。

所以它发生在我的案例中。希望它能节省一些人的时间。

【讨论】:

【参考方案6】:

你可以组成一个类,RestTemplateXML,它扩展了 RestTemplate。然后覆盖doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor&lt;T&gt;),并显式获取response-headers并将content-type设置为application/xml

现在 Spring 读取头文件并知道它是 `application/xml'。这是一种 hack,但它确实有效。

public class RestTemplateXML extends RestTemplate 

  @Override
  protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
        ResponseExtractor<T> responseExtractor) throws RestClientException 

     logger.info( RestTemplateXML.class.getSuperclass().getSimpleName() + ".doExecute() is overridden");

     Assert.notNull(url, "'url' must not be null");
     Assert.notNull(method, "'method' must not be null");
     ClientHttpResponse response = null;
     try 
        ClientHttpRequest request = createRequest(url, method);
        if (requestCallback != null) 
           requestCallback.doWithRequest(request);
        
        response = request.execute();

        // Set ContentType to XML
        response.getHeaders().setContentType(MediaType.APPLICATION_XML);

        if (!getErrorHandler().hasError(response)) 
           logResponseStatus(method, url, response);
        
        else 
           handleResponseError(method, url, response);
        
        if (responseExtractor != null) 
           return responseExtractor.extractData(response);
        
        else 
           return null;
        
     
     catch (IOException ex) 
        throw new ResourceAccessException("I/O error on " + method.name() +
              " request for \"" + url + "\":" + ex.getMessage(), ex);
     
     finally 
        if (response != null) 
           response.close();
        
     

  

  private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) 
     if (logger.isDebugEnabled()) 
        try 
           logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
                 response.getRawStatusCode() + " (" + response.getStatusText() + ")");
        
        catch (IOException e) 
           // ignore
        
     
  

  private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException 
     if (logger.isWarnEnabled()) 
        try 
           logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
                 response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
        
        catch (IOException e) 
           // ignore
        
     
     getErrorHandler().handleError(response);
  

【讨论】:

【参考方案7】:

试试这个:

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

【讨论】:

欢迎来到 Stack Overflow!虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。【参考方案8】:

或者你可以使用

public void setSupportedMediaTypes(List supportedMediaTypes)

属于AbstractHttpMessageConverter&lt;T&gt;的方法,添加一些你喜欢的ContentTypes。这种方式可以让MappingJackson2HttpMessageConvertercanRead()你的响应,并将其转换为你想要的类,在这种情况下,它是ProductList类。

我认为这一步应该与 Spring Context 初始化相关联。例如,通过使用

实现 ApplicationListener ...

【讨论】:

【参考方案9】:

Vadim Zin4uk's answer 的改进只是使用现有的 GsonHttpMessageConverter 类,但调用 setSupportedMediaTypes() 设置器。

对于 Spring Boot 应用程序,这会导致将以下内容添加到您的配置类中:

@Bean
public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) 
    GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
    converter.setGson(gson);
    List<MediaType> supportedMediaTypes = converter.getSupportedMediaTypes();
    if (! supportedMediaTypes.contains(TEXT_PLAIN)) 
        supportedMediaTypes = new ArrayList<>(supportedMediaTypes);
        supportedMediaTypes.add(TEXT_PLAIN);
        converter.setSupportedMediaTypes(supportedMediaTypes);
    
    return converter;

【讨论】:

【参考方案10】:

这不是在回答问题,但如果有人在偶然发现找不到合适的消息转换器的异常时提出这个问题,这是我的问题和解决方案。

在 Spring 4.0.9 中,我们能够发送这个

    JSONObject jsonCredential = new JSONObject();
    jsonCredential.put(APPLICATION_CREDENTIALS, data);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

ResponseEntity<String> res = restTemplate.exchange(myRestUrl), HttpMethod.POST,request, String.class);

在 Spring 4.3.5 版本中,我们开始看到错误消息,即未找到转换器。

转换器的工作方式是,如果您在类路径中有它,它们就会被注册。

Jackson-asl 仍在类路径中,但没有被 spring 识别。我们将 Jackson-asl 替换为 faster-xml jackson core。

添加后,我可以看到转换器正在注册。

【讨论】:

【参考方案11】:

我也有同样的错误信息: “无法提取响应:没有找到适合响应类型的 HttpMessageConverter ...”

当我试图从没有返回我想要转换的对象类型的链接中获取信息时,或者当链接没有返回任何内容时,就会发生这种情况。我使用 try catch 块处理它:

 try 
        status = restTemplate
            .getForObject(statusResourceUrl, Status.class);

        //TODO add new exceptions if necessary or simply use Exception
     catch (BeanCreationException | UnknownContentTypeException | HttpClientErrorException e) 
        status.setStatus("DOWN");
        System.out.println("exception " + e.getMessage());
    

【讨论】:

以上是关于没有找到适合响应类型的 HttpMessageConverter的主要内容,如果未能解决你的问题,请参考以下文章

使用杰克逊的 JSON 反序列化:没有找到适合类型的构造函数 - 可以提供默认构造函数或注释构造函数 [重复]

Jackson ObjectMapper 无法反序列化 POJO,抛出异常:没有找到适合类型 [...] 的构造函数:无法从 JSON 对象实例化

ASP.net JSON Webservice 响应类型的问题

思考响应式设计中的“缩放”

序列化 MirageJS 响应以适合 EF Core API 类型 List<>

杰克逊错误:没有适合简单类的构造函数