Java 找不到适合响应类型的 HttpMessageConverter

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21854369/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:04:46  来源:igfitidea点击:

no suitable HttpMessageConverter found for response type

javaxmlspringjaxb

提问by NimChimpsky

Using spring, with this code :

使用 spring,使用以下代码:

List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter httpMessageConverter : messageConverters){
  System.out.println(httpMessageConverter);
}
ResponseEntity<ProductList> productList = restTemplate.getForEntity(productDataUrl,ProductList.class);

I get

我得到

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.MappingHymanson2HttpMessageConverter@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]

The a snippet of the pojo :

pojo 的片段:

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

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

采纳答案by Sotirios Delimanolis

From a Spring point of view, none of the HttpMessageConverterinstances registered with the RestTemplatecan convert text/htmlcontent to a ProductListobject. The method of interest is HttpMessageConverter#canRead(Class, MediaType). The implementation for all of the above returns false, including Jaxb2RootElementHttpMessageConverter.

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

Since no HttpMessageConvertercan read your HTTP response, processing fails with an exception.

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

If you can control the server response, modify it to set the Content-typeto application/xml, text/xml, or something matching application/*+xml.

如果你能控制服务器响应,修改设置Content-typeapplication/xmltext/xml或东西匹配application/*+xml

If you don't control the server response, you'll need to write and register your own HttpMessageConverter(which can extend the Spring classes, see AbstractXmlHttpMessageConverterand its sub classes) that can read and convert text/html.

如果您不控制服务器响应,则需要编写和注册您自己的HttpMessageConverter(可以扩展 Spring 类,seeAbstractXmlHttpMessageConverter及其子类)可以读取和转换text/html.

回答by Vadim Zin4uk

If you can't change server media-type response, you can extend GsonHttpMessageConverter to process additional support types

如果您无法更改服务器媒体类型响应,则可以扩展 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);
    }
}

回答by Chester Leung

You can make up a class, RestTemplateXML, which extends RestTemplate. Then override doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>), and explicitly get response-headersand set content-typeto application/xml.

你可以组成一个类,RestTemplateXML,它扩展了RestTemplate。然后覆盖doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>),并显式获取response-headers并设置content-typeapplication/xml

Now Spring reads the headers and knows that it is `application/xml'. It is kind of a hack but it works.

现在 Spring 读取标头并知道它是“application/xml”。这是一种黑客,但它的工作原理。

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

回答by avidya

Or you can use

或者你可以使用

public void setSupportedMediaTypes(List supportedMediaTypes)

public void setSupportedMediaTypes(List supportedMediaTypes)

method which belongs to AbstractHttpMessageConverter<T>, to add some ContentTypesyou like. This way can let the MappingHymanson2HttpMessageConvertercanRead()your response, and transform it to your desired Class, which on this case,is ProductList Class.

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

and I think this step should hooked up with the Spring Context initializing. for example, by using

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

implements ApplicationListener { ... }

实现 ApplicationListener { ... }

回答by Leonardozm

Try this:

尝试这个:

<dependency>
    <groupId>com.fasterxml.Hymanson.core</groupId>
    <artifactId>Hymanson-databind</artifactId>
    <version>2.6.0</version>
</dependency>

回答by Wim Deblauwe

If you are using Spring Boot, you might want to make sure you have the Hymanson dependency in your classpath. You can do this manually via:

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

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

Or you can use the web starter:

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

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

回答by vsingh

This is not answering the problem but if anyone comes to this question when they stumble upon this exception of no suitable message converter found, here is my problem and solution.

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

In Spring 4.0.9, we were able to send this

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

In Spring 4.3.5 release, we starting seeing errors with the message that converter was not found. The way COnverets work is that if you have it in your classpath, they get registered.Hymanson-asl was still in classpath but was not being recognized by spring. We replace Hymanson-asl with faster-xml Hymanson core. Once we added I could see the converter being registered enter image description here

在 Spring 4.3.5 版本中,我们开始看到带有找不到转换器的消息的错误。COnverets 的工作方式是,如果你在你的类路径中有它,它们就会被注册。Hymanson-asl 仍然在类路径中,但没有被 spring 识别。我们用 fast-xml Hymanson core 替换了 Hymanson-asl。添加后,我可以看到正在注册的转换器在此处输入图片说明

回答by user1913596

In addition to all the answers, if you happen to receive in response text/htmlwhile you've expected something else (i.e. application/json), it may suggest that an error occurred on the server side (say 404) and the error page was returned instead of your data.

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

So it happened in my case. Hope it will save somebody's time.

所以它发生在我的情况下。希望它会节省某人的时间。

回答by Guillaume Berche

A refinement of Vadim Zin4uk's answeris just to use the existing GsonHttpMessageConverter class but invoke the setSupportedMediaTypes() setter.

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

For spring boot apps, this results into adding to following to your configuration classes:

对于 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;
}

回答by Markoorn

You could also simply tell your RestTemplateto accept all media types:

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

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

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

   return restTemplate;
}