I've upgraded from SpringBoot 2.4.6 to 2.5.1, and noticed the following bug / regression issue regarding RestTemplate. All my tests started to fail because the framework creates XML body instead of JSON.
In 2.4.6, I don't specify any ContentType for each request. When I send an HTTP request, the framework converts my object to JSON body. Here is a sample code:
HttpHeaders headers = addBearerAuthHeader(username); // sets AUTHORIZATION header
ResponseEntity<AccountResource> response = restTemplate.exchange("/sets/" + setId + "/accounts", POST,
new HttpEntity<>(accountDTO, headers), AccountResource.class);
````
Body is created as standard JSON:
```json
{"name":"tfWqyuUgGZ","basePlatformId":"platform-5466","description":null,"targetId":70310,"setId":"109c05c8-0be0-4e32-8122-cbfd1f1f8785","properties":{"address":"129.83.21.137","username":"DInuyP","policyId":56647,"secret":"5dSlX7Rn"}}
After upgrading to 2.5.X, the framework started creating XML instead of JSON:
<AccountDTO><name>I4rDnGaJkL</name><basePlatformId>platform-9615</basePlatformId><description/><targetId>88192</targetId><setId>109c05c8-0be0-4e32-8122-cbfd1f1f8785</setId><properties><address>251.172.20.153</address><username>TFWSBI</username><policyId>93423</policyId><secret>IB6WlQqq</secret></properties></AccountDTO>
To work around this issue, I can add the following line to the code above:
headers.setContentType(MediaType.APPLICATION_JSON);
However, this is not a very good solution, since now I have to change a lot of places in my code.
Unfortunately, changing a single place by using an HTTP interceptor (ClientHttpRequestInterceptor
) to set the header isn't possible, because Object to Body conversion occurs before interceptor is called.
Another workaround I found after digging the code, is utilizing system property
-Dspring.xml.ignore=true
.
However, this is not perfect in case the code is handling both JSON and XML data.
I've started debugging the issue and noticed the difference between the SpringBoot versions and how this can be explained. The conversion starts in: https://github.com/spring-projects/spring-framework/blob/4a13928a2721259f9f4cf06814a6cfee6667bbb5/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java#L973
In SpringBoot 2.4.6, when printing getMessageConverters().toStoring()
, I get:
[
org.springframework.http.converter.ByteArrayHttpMessageConverter@6e78fcf5,
org.springframework.http.converter.StringHttpMessageConverter@56febdc,
org.springframework.http.converter.ResourceHttpMessageConverter@3b8ee898,
org.springframework.http.converter.xml.SourceHttpMessageConverter@7d151a,
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@294bdeb4,
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@5300f14a,
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@1f86099a,
org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter@77bb0ab5,
org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter@f2c488
]
However, in the same code running in 2.5.X, notice the difference: [ org.springframework.http.converter.ByteArrayHttpMessageConverter@452c8a40, org.springframework.http.converter.StringHttpMessageConverter@534243e4, org.springframework.http.converter.ResourceHttpMessageConverter@29006752, org.springframework.http.converter.xml.SourceHttpMessageConverter@470a9030, org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@66d57c1b, org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter@27494e46, org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@d59970a, org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter@1e411d81, org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter@53b98ff6 ]
The reason for the difference in converters is this line:
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
https://github.com/spring-projects/spring-framework/blob/4a13928a2721259f9f4cf06814a6cfee6667bbb5/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java#L127
In my sample, requestContentType = null
in runtime, since I don't initialize any specific contentType.
Therefore, the first converter that matches the conditions is MappingJackson2XmlHttpMessageConverter
.
I don't know why there is a difference between the class loaders and why jackson2XmlPresent=true
on 2.5.X while it is false
on 2.4.X.
I haven't changed anything in my code or in the project dependencies except the SpringBoot version.
Maybe there is a workaround, but IMHO it is still a breaking change between 2.5.X to 2.4.X.
Thanks, Ori.
Comment From: bclozel
Judging by your description, this issue seems to be triggered by several elements, possibly unrelated.
Could you create a sample application that we can git clone and run (or just add it as an attachment here) so that we can take a look? Ideally, a very simple application that works with json and then shows the issue when upgraded. You can use httpbin.org for a test case.
Thanks!
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.
Comment From: vizmi
while this is a closed topic, google dropped me here after fighting the same issue. What worked me is to replace the converters list with a singleton list of json converter, something like this:
restTemplate.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
Comment From: kimatia
Had similar challenge was running framework 2.5.4 with java 17 which returned xml, did run project with lower version(11) and response properly formatted to json
Comment From: HMLTL
Same issue =(
Comment From: hermanwongkm
Any one solved it?