【问题标题】:Spring RestTemplate cannot unmarshal XML containing "<!DOCTYPE ... dtd>"Spring RestTemplate 无法解组包含“<!DOCTYPE ... dtd>”的 XML
【发布时间】:2018-05-30 22:41:23
【问题描述】:

我调用了第三方提供的旧网络服务。我正在使用 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 的自动行为的情况下自定义解组?我想强制解组器接受包含带有 DTD 引用的 XML 元素。

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

【问题讨论】:

  • 只需将该属性设置为 false... 您可以将 HttpMessageConverter 配置为这样做 docs.spring.io/spring/docs/current/javadoc-api/org/… 。默认情况下,此属性设置为 false 禁用 DTD 支持,这与启用 DTD 支持时的安全隐患有关。
  • @M.Deinum 它就像魔术一样工作(假设您的意思是将该属性设置为 true :)),谢谢!我以前没有注意到这个方法。请给它一个答案,我很乐意接受它!你对我的相关问题stackoverflow.com/q/47836310/2886891也有这么简单的答案吗? Alex Savitsky 的回答确实有效,但对我来说更像是一个 hack。
  • 在此处查看答案,尤其是第二部分也应该回答您的其他问题。

标签: java xml spring unmarshalling resttemplate


【解决方案1】:

默认情况下,Jaxb2RootElementHttpMessageConverter 禁用 DTD 支持(以及 XML 实体支持)。这样做的原因是它具有安全隐患,请参阅SPR-11376

要在Jaxb2RootElementHttpMessageConverter 上启用它,您可以将supportDtd 属性设置为true 以再次启用它。但请注意,这也会带来一些潜在的安全问题!

@Bean
public Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter() {
    Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter();
    converter.setSupportDtd(true);
    return converter;
}

这应该足以(重新)配置支持,而无需添加任何其他配置。要记住的一件事是,这将配置全局可用的Jaxb2RootElementHttpMessageConverter,因此会影响您可能想要使用的所有控制器和RestTemplates。

您也可以使用RestTemplateBuilder 而不是这样做,在创建RestTemplate 的实例时应该使用它来仅影响特定的RestTemplate

@Bean
public RestTemplate yourRestTemplate(RestTemplateBuilder builder) {
    Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter();
    converter.setSupportDtd(true);

    return builder.messageConverters(converter).build()
}

通过这种方式,您可以针对RestTemplate 的该实例进行特定配置,并配置您喜欢的内容。

【讨论】:

  • 您现在能否在您的回答中做一个总结,我们将从这里删除我们的 cmets,以便我们为其他读者保持清晰。然后我会接受你的回答,这对我很有帮助,谢谢!
  • 我应该修改什么... 答案是正确的。您的结果不同,因为您使用 RestTemplateBuilder 的方式不应该使用它。
  • 如果您使用RestTemplateBuilder 作为(默认)bean 或手动实例化它,可能会添加一些关于行为有何不同的注释。因为我不知道有additionalMessageConverters()messageConverters()defaultMessageConverters()这样的方法,其他人也可以从中受益。
猜你喜欢
  • 1970-01-01
  • 2018-03-28
  • 2018-07-16
  • 2017-07-01
  • 2014-12-26
  • 1970-01-01
  • 2014-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多