Using spring, with this code :
List> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter ht
A refinement of Vadim Zin4uk's answer is just to use the existing GsonHttpMessageConverter class but invoke the setSupportedMediaTypes() setter.
For spring boot apps, this results into adding to following to your configuration classes:
@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;
}
From a Spring point of view, none of the HttpMessageConverter
instances registered with the RestTemplate
can convert text/html
content to a ProductList
object. The method of interest is HttpMessageConverter#canRead(Class, MediaType)
. The implementation for all of the above returns false
, including Jaxb2RootElementHttpMessageConverter
.
Since no HttpMessageConverter
can read your HTTP response, processing fails with an exception.
If you can control the server response, modify it to set the Content-type
to application/xml
, text/xml
, or something matching 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 AbstractXmlHttpMessageConverter
and its sub classes) that can read and convert text/html
.
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
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.jackson-asl was still in classpath but was not being recognized by spring. We replace Jackson-asl with faster-xml jackson core. Once we added I could see the converter being registered
You can make up a class, RestTemplateXML, which extends RestTemplate. Then override doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>)
, and explicitly get response-headers
and set content-type
to application/xml
.
Now Spring reads the headers and knows that it is `application/xml'. It is kind of a hack but it works.
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);
}
}
Or you can use
public void setSupportedMediaTypes(List supportedMediaTypes)
method which belongs to AbstractHttpMessageConverter<T>
, to add some ContentTypes
you like. This way can let the MappingJackson2HttpMessageConverter
canRead()
your response, and transform it to your desired Class, which on this case,is ProductList Class.
and I think this step should hooked up with the Spring Context initializing. for example, by using
implements ApplicationListener { ... }
In addition to all the answers, if you happen to receive in response text/html
while 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.
So it happened in my case. Hope it will save somebody's time.