Verwenden Sie den Frühling mit diesem Code:
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter httpMessageConverter : messageConverters){
System.out.println(httpMessageConverter);
}
ResponseEntity<ProductList> productList = restTemplate.getForEntity(productDataUrl,ProductList.class);
Ich bekomme
o[email protected]34649ee4
[email protected]fba59b
[email protected]383580da
or[email protected]409e850a
org.springframework[email protected]673074aa
org.springfr[email protected]1e3b79d3
org.springfr[email protected]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]
Ein Ausschnitt des Pojo:
@XmlRootElement(name="TheProductList")
public class ProductList {
@XmlElement(required = true, name = "date")
private LocalDate importDate;
Aus der Frühlingssicht kann keine der mit HttpMessageConverter
registrierten RestTemplate
-Instanzen text/html
-Inhalt in ein ProductList
-Objekt konvertieren. Die Methode von Interesse ist HttpMessageConverter#canRead(Class, MediaType)
. Die Implementierung für alle oben genannten Ergebnisse gibt false
zurück, einschließlich Jaxb2RootElementHttpMessageConverter
.
Da keine HttpMessageConverter
Ihre HTTP-Antwort lesen kann, schlägt die Verarbeitung mit einer Ausnahme fehl.
Wenn Sie die Serverantwort steuern können, ändern Sie sie, um den Content-type
auf application/xml
, text/xml
oder etwas anderes festzulegen, das application/*+xml
entspricht.
Wenn Sie die Serverantwort nicht steuern, müssen Sie Ihre eigene HttpMessageConverter
schreiben und registrieren (die Spring-Klassen erweitern kann, siehe AbstractXmlHttpMessageConverter
und ihre Unterklassen), die text/html
lesen und konvertieren kann.
Wenn Sie die Antwort des Servermedientyps nicht ändern können, können Sie GsonHttpMessageConverter um die Verarbeitung zusätzlicher Supporttypen erweitern
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);
}
}
Wenn Sie Spring Boot verwenden, möchten Sie möglicherweise sicherstellen, dass Sie die Jackson-Abhängigkeit in Ihrem Klassenpfad haben. Sie können dies manuell tun über:
<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>
Oder Sie können den Webstarter verwenden:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Sie können die Klasse RestTemplateXML erstellen, die RestTemplate erweitert. Dann überschreiben Sie doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>)
und erhalten Sie explizit response-headers
und setzen Sie content-type
auf application/xml
.
Nun liest Spring die Header und weiß, dass es sich um "application/xml" handelt. Es ist eine Art Hack, aber es funktioniert.
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);
}
}
Sie können Ihrem RestTemplate
auch einfach anweisen, alle Medientypen zu akzeptieren:
@Bean
public RestTemplate restTemplate() {
return new 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;
}
Wenn Sie zusätzlich zu allen Antworten in Antwort text/html
empfangen werden, während Sie etwas anderes erwartet haben (z. B. application/json
), kann dies auf einen Fehler auf der Serverseite (z. B. 404) hinweisen und die Fehlerseite wurde statt zurückgegeben deine Daten.
So ist es in meinem Fall passiert. Ich hoffe, es wird jemandem Zeit sparen.
Oder du kannst verwenden
public void setSupportedMediaTypes (Liste unterstützterMediaTypes)
methode, die zu AbstractHttpMessageConverter<T>
gehört, um eine ContentTypes
hinzuzufügen, die Ihnen gefällt. Auf diese Weise kann die MappingJackson2HttpMessageConverter
canRead()
Ihre Antwort erhalten und diese in Ihre gewünschte Klasse umwandeln, die in diesem Fall ProductList-Klasse ist.
ich denke, dieser Schritt sollte mit der Initialisierung des Spring Context verbunden sein. zum Beispiel mit
implementiert ApplicationListener { ... }
Versuche dies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
Eine Verfeinerung von Vadim Zin4uks Antwort dient nur der Verwendung der vorhandenen GsonHttpMessageConverter-Klasse, ruft jedoch den setSupportedMediaTypes () - Setter auf.
Für Spring Boot-Apps führt dies dazu, dass folgende Konfigurationsklassen hinzugefügt werden:
@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;
}