Spring @RestController создает XML без пространств имен - PullRequest
0 голосов
/ 11 сентября 2018

У меня есть @RestController, который должен возвращать результат от веб-службы SOAP. Клиентские классы веб-службы генерируются с помощью 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>
    ...

Но если что-то идет не так (то есть конечная точка мыла недоступна) и генерируется исключение, контроллер 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>

Кто-нибудь знает, что мне нужно сделать, чтобы получить либо пространства имен с Джексоном, либо ошибку, преобразованную в XML с помощью JAXB?

1 Ответ

0 голосов
/ 13 сентября 2018

Я обнаружил, что пружина автоматически создает LinkedHashMap с подробностями ошибки.Когда эту карту необходимо преобразовать в html без jackson-xml в classpath, статус http 406 возвращается из-за отсутствия конвертера для LinkedHashMap в html.Поэтому мое решение заключается в добавлении простого 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());
    }
}
...