web-dev-qa-db-fra.com

aucun convertisseur HttpMessageConverter approprié trouvé pour le type de réponse

En utilisant un ressort, avec ce code:

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

Je reçois 

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]

Le bout du pojo:

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

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

Du point de vue de Spring, aucune des instances HttpMessageConverter inscrites avec RestTemplate ne peut convertir le contenu text/html en un objet ProductList. La méthode d'intérêt est HttpMessageConverter#canRead(Class, MediaType). L'implémentation de tous les éléments ci-dessus renvoie false, y compris Jaxb2RootElementHttpMessageConverter

Etant donné que HttpMessageConverter ne peut pas lire votre réponse HTTP, le traitement échoue avec une exception.

Si vous pouvez contrôler la réponse du serveur, modifiez-la pour définir le Content-type sur application/xml, text/xml ou un autre paramètre correspondant à application/*+xml.

Si vous ne contrôlez pas la réponse du serveur, vous devrez écrire et enregistrer votre propre HttpMessageConverter (qui peut étendre les classes Spring, voir AbstractXmlHttpMessageConverter et ses sous-classes) pouvant lire et convertir text/html.

26
Sotirios Delimanolis

Si vous ne pouvez pas modifier la réponse du type de média du serveur, vous pouvez étendre GsonHttpMessageConverter pour traiter d'autres types de support.

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);
    }
}
8
Vadim Zin4uk

Si vous utilisez Spring Boot, vous voudrez peut-être vous assurer que la dépendance de Jackson est dans votre chemin de classe. Vous pouvez le faire manuellement via:

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

Ou vous pouvez utiliser le démarreur Web:

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

Vous pouvez constituer une classe, RestTemplateXML, qui étend RestTemplate. Ensuite, remplacez doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>) et obtenez explicitement response-headers et définissez content-type sur application/xml

Maintenant, Spring lit les en-têtes et sait qu'il s'agit de 'application/xml'. C'est un peu un bidouillage mais ça marche.

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);
  }
}
3
Chester Leung

En plus de toutes les réponses, si vous recevez dans la réponse text/html alors que vous attendiez quelque chose d'autre (par exemple, application/json), cela peut suggérer qu'une erreur s'est produite côté serveur (par exemple, 404) et que la page d'erreur a été renvoyée au lieu de vos données.

C'est donc arrivé dans mon cas. J'espère que cela fera gagner du temps à quelqu'un.

1
user1913596

Ou vous pouvez utiliser 

public void setSupportedMediaTypes (liste prise en chargeMediaTypes) 

méthode qui appartient à AbstractHttpMessageConverter<T>, pour ajouter de la ContentTypes que vous aimez. De cette façon, vous pouvez laisser la réponse MappingJackson2HttpMessageConvertercanRead() et la transformer en votre classe souhaitée, qui dans ce cas est ProductList Class.

et je pense que cette étape devrait être liée à l'initialisation du contexte de printemps. par exemple, en utilisant 

implémente ApplicationListener { ... }

0
avidya

Essaye ça:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.6.0</version>
</dependency>
0
Leonardozm

Un raffinement de La réponse de Vadim Zin4uk consiste simplement à utiliser la classe GsonHttpMessageConverter existante, mais à appeler le paramètre setSupportedMediaType ().

Pour les applications de démarrage printanier, cela revient à ajouter ce qui suit à vos classes de configuration:

@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;
}
0
Guillaume Berche