-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Description
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:
{"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:
spring-framework/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java
Line 973 in 4a13928
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) { |
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);
spring-framework/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java
Line 127 in 4a13928
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); |
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.