Spring @RestController 生成 XML 没有命名空间
Spring @RestController produces XML without namespaces
我有一个 @RestController
应该 return 来自 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 控制器 returns 一个 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 会自动创建一个包含错误详细信息的 LinkedHashMap
。当此映射应转换为 html 而类路径上没有 jackson-xml 时,http 状态 406
由于缺少 LinkedHashMap
到 [=22] 的转换器而返回=].所以我的解决方案是向我的应用程序添加一个简单的 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());
}
}
我有一个 @RestController
应该 return 来自 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 控制器 returns 一个 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 会自动创建一个包含错误详细信息的 LinkedHashMap
。当此映射应转换为 html 而类路径上没有 jackson-xml 时,http 状态 406
由于缺少 LinkedHashMap
到 [=22] 的转换器而返回=].所以我的解决方案是向我的应用程序添加一个简单的 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());
}
}