Spring RestTemplate无法解组包含""的XML。

时间:2017-12-17 10:08:11

标签: java xml spring unmarshalling resttemplate

我称之为第三方提供的旧网络服务。我正在使用Spring RestTemplate

HttpEntity<MyRequest> requestHttpEntity = new HttpEntity<>(requestBody, headers);
MyResponse response = restTemplate.postForEntity(url, requestHttpEntity, MyResponse.class);

我收到一份XML(我不能影响的格式,它是第三方服务)作为回复:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE MyResponse SYSTEM "http://example.com:8080/some/path/MyResponse.dtd">

<MyResponse>
    ...
</MyResponse>

postForEntity()方法抛出异常

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.]

我在这里找到http://apache.org/xml/features/disallow-doctype-decl功能唯一明智的参考:https://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl

问题:如何在不完全避免Spring RestTemplate的自动行为的情况下自定义解组?我想强制unmarshaler接受包含DTD引用的XML元素。

这个问题与我的其他问题How to customize automatic marshaling in Spring RestTemplate to produce/modify XML headers (encoding, DOCTYPE)密切相关,但此处提出的解决方案并不容易适用。

1 个答案:

答案 0 :(得分:3)

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 RestTemplates 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.