【问题标题】:Spring @RestController produces XML without namespacesSpring @RestController 生成没有命名空间的 XML
【发布时间】:2019-02-15 18:43:08
【问题描述】:

我有一个@RestController,它应该从 SOAP Web 服务返回结果。 Web 服务客户端类是使用 maven-jaxb2-plugin 生成的,因此使用 JAXB 注释。

@RestController
public class ZemisPersonSearchController {

    @Autowired(required = true)
    private SoapClient soapClient;

    @RequestMapping(path = "/api/persons/{no}", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_XML_VALUE })
    @PreAuthorize("hasRole('ROLE_GET_PERSON_DETAILS')")
    public ResponseEntity<Object> getPersonDetails(HttpServletRequest httpReq, @PathVariable String no) {
        Result result = soapClient.getPersonDetails(UUID.randomUUID().toString(), no);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "responseHeader",
    "getPersonDetailsResponse",
    "searchPersonResponse",
    "systemException"
})
@XmlRootElement(name = "result")
public class Result {

    @XmlElement(name = "ResponseHeader")
    protected ResponseHeaderType responseHeader;
    @XmlElement(name = "GetPersonDetailsResponse")
    protected PersonType getPersonDetailsResponse;
    @XmlElement(name = "SearchPersonResponse")
    protected SearchPersonResponseType searchPersonResponse;
    @XmlElement(name = "SystemException")
    protected FaultInfoType systemException;
...

只要一切按预期工作,结果如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:result
    xmlns:ns2="http://mynamespace/personsearchservice/v1">
    <ns2:ResponseHeader>
    ...

但如果出现问题(即,soap 端点不可用)并引发异常,REST 控制器将返回 406 http 状态,因为无法将自动生成的响应转换为 XML。

我尝试使用 Jackson XML 扩展我的应用程序,并按照我找到的文档和博客中的建议注册了模块以处理 JAXB 注释。

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
@Bean
public Module jaxbModule() {
    return new JaxbAnnotationModule();
}

但是如果我这样做了,现在可以将异常错误生成为 XML,并且我得到了正确的 http 状态 500,而且没有发生错误时的响应也不再包含命名空间,并且保留命名空间很重要因为它是一个大而复杂的 xml:

<result>
    <ResponseHeader>

有没有人知道我必须做什么才能使用 jackson 获取命名空间或使用 JAXB 将错误转换为 xml?

【问题讨论】:

    标签: spring spring-boot jackson jaxb spring-data-rest


    【解决方案1】:

    我发现,spring 会自动创建一个带有错误详细信息的LinkedHashMap。当此映射应在类路径上不带 jackson-xml 的情况下转换为 html 时,由于缺少将 LinkedHashMap 转换为 html 的转换器,返回 http 状态 406。所以我的解决方案是在我的应用程序中添加一个简单的AbstractHttpMessageConverter,它将带有错误详细信息的地图转换为html。因此,我不需要在类路径中使用 jackson-xml,并且我的 XML 是从 JAXB 生成的,其中包含命名空间。

    转换器:

    public class HttpXmlExceptionConverter extends AbstractHttpMessageConverter<Map<String, Object>> {
    
        public HttpXmlExceptionConverter() {
            super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml"));
        }
    
        @Override
        protected boolean supports(Class<?> clazz) {
            return Map.class.isAssignableFrom(clazz);
        }
    
        @Override
        protected Map readInternal(Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage)
                throws IOException, HttpMessageNotReadableException {
            throw new NotImplementedException("readFromSource is not supported!");
        }
    
        @Override
        protected void writeInternal(Map<String, Object> map, HttpOutputMessage outputMessage)
                throws IOException, HttpMessageNotWritableException {
            OutputStream s = outputMessage.getBody();
            StringBuilder sb = new StringBuilder();
            sb.append("<map>");
            for (Entry<String, Object> entry : map.entrySet()) {
                sb.append("<").append(entry.getKey()).append(">");
                if (entry.getValue() != null)
                    sb.append(StringEscapeUtils.escapeXml(entry.getValue().toString()));
                sb.append("</").append(entry.getKey()).append(">");
            }
            sb.append("</map>");
            s.write(sb.toString().getBytes(Charset.defaultCharset()));
        }
    
    }
    

    并注册转换器:

    @Configuration
    //@EnableWebMvc
    // -> EnableWebMvc is required if you don't want the spring boot auto configuration should be extended
    public class WebMvcConfig implements WebMvcConfigurer {
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(new HttpXmlExceptionConverter());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-14
      • 2018-12-07
      • 1970-01-01
      • 2013-03-16
      相关资源
      最近更新 更多