Spring Boot REST 端点忽略 Content-Type

Posted

技术标签:

【中文标题】Spring Boot REST 端点忽略 Content-Type【英文标题】:Spring Boot REST endpoint ignores Content-Type 【发布时间】:2020-10-27 21:34:07 【问题描述】:

我在创建具有 application/xml 类型的简单 REST 控制器时遇到问题。 我正在使用 Kotlin 并通过在 Gradle KTS 中提供依赖项来注册 Jackson 转换器

“实施”(组=“com.fasterxml.jackson.dataformat”,名称=“jackson-dataformat-xml”,版本=jacksonXmlVersion)

Controller 使用 Swagger,看起来像这样:

@ApiOperation(
    value = "Epcis capture point",
    response = EPCISDocResponse::class,
    httpMethod = "POST"
)
@PostMapping(
    "/capture",
    consumes = [MediaType.APPLICATION_XML_VALUE],
    produces = [MediaType.APPLICATION_XML_VALUE]
)
fun capture(@RequestBody request: EPCISDocumentType): ResponseEntity<EPCISDocResponse> 
    doSomething()

我的模型类是由 XJC 生成的,看起来像这样:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "EPCISDocumentType", propOrder = 
    "epcisHeader",
    "epcisBody",
    "extension",
    "any"
)
@XmlRootElement(name = "EPCISDocument", namespace = "urn:epcglobal:epcis:xsd:1")
public class EPCISDocumentType
    extends Document

问题是 Spring Boot 找不到 application/xml 对应的转换器,并尝试将其解析为 JSON,给我下一个错误:

.w.s.m.s.DefaultHandlerExceptionResolver:已解决 [org.springframework.http.converter.HttpMessageNotReadableException:JSON 解析错误:无法反序列化 java.util.ArrayList&lt;java.lang.Object&gt; 的实例,超出 VALUE_STRING 令牌;嵌套异常是 com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.util.ArrayList&lt;java.lang.Object&gt; out of VALUE_STRING token 在 [来源:(PushbackInputStream);行:4,列:2](通过引用链:epcis.capture.EPCISDocumentType["any"])]

我为 SOAPTemplate 定义了一个 Jaxb2 编组器,它可能会干扰:

@Bean
fun marshaller(): Jaxb2Marshaller? 
    val marshaller = Jaxb2Marshaller()
    marshaller.setClassesToBeBound(
        epcis.capture.EPCISDocumentType::class.java,
        CaptureEPCISDocResponse::class.java,
        LeanEPCISDocResponse::class.java
    )
    return marshaller

非常感谢任何帮助,这是我第一次看到杰克逊映射到内容类型的问题。

在应用程序上下文中注册的转换器列表:

org.springframework.http.converter.StringHttpMessageConverter@531bfe4b
org.springframework.http.converter.ResourceHttpMessageConverter@2c8f7d6a
org.springframework.http.converter.ResourceRegionHttpMessageConverter@77331437
org.springframework.http.converter.xml.SourceHttpMessageConverter@98a0842
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@56f569e
org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter@525c0f74
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@3da12ba4

更新

我能够通过删除 MappingJackson2XmlHttpMessageConverter 来解决这个问题,因为我的模型包含 XmlRootElement 并且应该由 Jaxb2RootElementHttpMessageConverter 编组,但现在问题更加奇怪了。 现在,Jaxb2Root 转换器没有将它编组到我的对象中,而是为我的控制器提供了 JAXBElement:

Method [public org.springframework.http.ResponseEntity<com.movilizer.lean.model.epcis.LeanEPCISDocResponse> com.movilizer.lean.leanepcis.controller.EpcisController.capture(epcis.capture.EPCISDocumentType)] with argument values:
 [0] [type=javax.xml.bind.JAXBElement] [value=javax.xml.bind.JAXBElement@76e6ecc4] ] with root cause

根据我的发现,似乎是特定的 sun jaxb-impl 负责此问题,因此解析器提供 JAXBElement 而不是有效负载本身。 有什么帮助吗?

【问题讨论】:

【参考方案1】:

我放弃了让 Jaxb2RootElementHttpMessageConverter 正常工作的想法,所以我只是将我的 rest 控制器更改为接受 String 值:

private val unmarshaller = javax.xml.bind.JAXBContext.newInstance(EPCISDocumentType::class.java).createUnmarshaller()

@PostMapping(
    "/capture",
    consumes = [MediaType.APPLICATION_XML_VALUE],
    produces = [MediaType.APPLICATION_XML_VALUE]
)
fun capture(@RequestBody payload: String): ResponseEntity<String> 
    val request = (unmarshaller.unmarshal(StringReader(payload)) as JAXBElement<EPCISDocumentType>).value

遗憾的是,我对应该开箱即用的调试工具不感兴趣,但不感兴趣。 我想要一个“有效”的解决方案,但不实现自定义转换器,但看起来 JAXB 特定的实现和 Spring 集成有问题。

【讨论】:

以上是关于Spring Boot REST 端点忽略 Content-Type的主要内容,如果未能解决你的问题,请参考以下文章

没有 REST 端点在 Spring Boot 应用程序中工作

为啥 spring-boot-starter-jdbc 会破坏我的 REST 端点?

Spring-Boot REST 服务基本 http 身份验证排除一个端点

列出所有已部署的 REST 端点(spring-boot、jersey)

Spring Boot REST API 端点映射最佳实践

docker 镜像正在运行,但无法在 Spring Boot 应用程序中访问 REST 端点