问题
I call an old web service provided by a third party. I am using Spring RestTemplate
:
HttpEntity<MyRequest> requestHttpEntity = new HttpEntity<>(requestBody, headers);
MyResponse response = restTemplate.postForEntity(url, requestHttpEntity, MyResponse.class);
I receive an XML (which format I cannot influence, it's a third party service) as a response:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE MyResponse SYSTEM "http://example.com:8080/some/path/MyResponse.dtd">
<MyResponse>
...
</MyResponse>
The postForEntity()
method throws the exception
org.springframework.web.client.RestClientException:
Error while extracting response for type [class com.example.MyResponse] and content type [text/xml;charset=ISO-8859-1];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException:
Could not unmarshal to [class com.example.MyResponse]: null;
nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 10;
DOCTYPE is disallowed when the feature
"http://apache.org/xml/features/disallow-doctype-decl" set to true.]
I found the only sensible reference to the http://apache.org/xml/features/disallow-doctype-decl
feature here: https://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
Question: How can I customize the unmarshaling without completely avoiding the automagic behavior of Spring RestTemplate? I want to force the unmarshaler to accept XML containing element with DTD reference.
This question is strongly related to my other question How to customize automatic marshaling in Spring RestTemplate to produce/modify XML headers (encoding, DOCTYPE), but the solution proposed there is not easily applicable here.
回答1:
By default the Jaxb2RootElementHttpMessageConverter disables the DTD support (and with that XML Entity support). The reason for this is that it has security implications, see SPR-11376.
To enable it on the Jaxb2RootElementHttpMessageConverter you can set the supportDtd
property to true
to enable it again. But be aware this will also open up some potential security issues!.
@Bean
public Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter() {
Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter();
converter.setSupportDtd(true);
return converter;
}
This should be enough to (re)configure support without needing to add any additional configuration. One thing to remember is that this will configure the globally available Jaxb2RootElementHttpMessageConverter
and as such will impact all controllers and RestTemplate
s you might want to use.
Instead of doing this you could also use the RestTemplateBuilder
which you should use when creating an instance of the RestTemplate
to only influence that specific RestTemplate
.
@Bean
public RestTemplate yourRestTemplate(RestTemplateBuilder builder) {
Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter();
converter.setSupportDtd(true);
return builder.messageConverters(converter).build()
}
This way you can configure it specific for that instance of the RestTemplate
and configure what you like.
来源:https://stackoverflow.com/questions/47854054/spring-resttemplate-cannot-unmarshal-xml-containing-doctype-dtd