Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring RestTemplate message converter priority when posting

What is the most convenient way to influence the priority of the message converters Spring applies when POSTing with RestTemplate?

Use case: I want to ensure a given entity is POSTed as JSON rather than e.g. XML when I do restTemplate.postForEntity(url, entity, Void.class).

Default

By default the entity is converted to XML because the MappingJackson2XmlHttpMessageConverter takes precedence over the MappingJackson2HttpMessageConverter. The default list of converters for my app appears to be (Spring scans the classpath to see what's available): enter image description here

Option 1

You can configure the message converters explicitly for a given RestTemplate instance like so restTemplate.setMessageConverters(Lists.newArrayList(new MappingJackson2HttpMessageConverter())). This is inconvenient if the instance is shared (as a Spring bean for example) as you might need converter X in one case and converter Y in a different one.

Option 2

You can set Accept and Content-Type HTTP headers explicitly in which case Spring will use a matching message converter. The downside is that you have to resort to RestTemplate.exchange instead of RestTemplate.postForEntity which means: extra code, less convenience.

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
HttpEntity requestEntity = new HttpEntity(entity, requestHeaders);
restTemplate.exchange(url, HttpMethod.POST, requestEntity, Void.class);

Option 3

This might be what I'm looking for :)

like image 543
Marcel Stör Avatar asked Dec 19 '17 20:12

Marcel Stör


2 Answers

This issue is answered in detail here.

Basically, when you add the below-mentioned library, it adds MappingJackson2XmlHttpMessageConverter before MappingJackson2HttpMessageConverter. As a result, Spring boot assumes every request accepts application/XML.

 <dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

To avoid this behaviour, you might want to swap the two message converters.

Example:

  @Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// move XML converter to the end of list
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for (int i = 0; i < messageConverters.size() -1 ; i++) {
    if (messageConverters.get(i) instanceof MappingJackson2XmlHttpMessageConverter) {
        Collections.swap(messageConverters, i,messageConverters.size() - 1);
    }
}

restTemplate.setMessageConverters(messageConverters);

// add interceptors if necessary
restTemplate.setInterceptors(Collections.singletonList(catalogInterceptior()));
return restTemplate;

}

like image 195
Irshad Avatar answered Nov 07 '22 03:11

Irshad


According to the Spring javadoc (https://docs.spring.io/spring-framework/docs/current/javadoc-api/index.html?org/springframework/web/client/RestTemplate.html) you can still use postForEntity,

public <T> ResponseEntity<T> postForEntity(java.lang.String url,
                                       @Nullable
                                       java.lang.Object request,
                                       java.lang.Class<T> responseType,
                                       java.util.Map<java.lang.String,?> uriVariables)
                                throws RestClientException
....

The request parameter can be a HttpEntity in order to add additional HTTP headers to the request.

like image 1
Javier Avatar answered Nov 07 '22 03:11

Javier