You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot开发SOAP-REST中转服务及SOAP转对象报错求助

SOAP转REST中间Wrapper服务问题排查

需求背景

开发一个中间Wrapper服务,流程为:接收SOAP请求消息并转换为JSON,调用REST API,将返回的JSON响应转换为SOAP响应后返回给调用方,实现SOAP与REST通信的中转。已有对应的WSDL,基于Spring Boot开发,目前在SOAP消息映射到生成类时出错。

当前实现代码

@RestController
public class WrapperController {

  @PostMapping(
      value = "/wrap",
      consumes = MediaType.APPLICATION_XML_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public Q248Req wrapSOAPRequest(@RequestBody String SOAPReq)
      throws SOAPException, IOException, JAXBException {
    System.out.println("SOAP Request: " + SOAPReq);
    SOAPMessage message =
        MessageFactory.newInstance()
            .createMessage(null, new ByteArrayInputStream(SOAPReq.getBytes()));
    Unmarshaller unmarshaller = JAXBContext.newInstance(TestObj.class).createUnmarshaller();
    TestObj Req =
        (TestObj) unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument());

    return Req;
  }
}

错误信息

SOAP Body: [#document: null]
2022-11-14 12:36:43.888 ERROR 21596 --- [nio-9080-exec-3] c.s.x.m.s.s.impl                         : SAAJ0251: 无法从Body中提取Document
2022-11-14 12:36:43.896 ERROR 21596 --- [nio-9080-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/wrapper-service/v1/decisions/q248] threw exception [Request processing failed; nested exception is com.sun.xml.messaging.saaj.SOAPExceptionImpl: Unable to extract Document from body] with root cause

java.lang.ClassCastException: class com.sun.org.apache.xerces.internal.dom.TextImpl cannot be cast to class org.w3c.dom.Element (com.sun.org.apache.xerces.internal.dom.TextImpl and org.w3c.dom.Element are in module java.xml of loader 'bootstrap')
    at com.sun.xml.messaging.saaj.soap.impl.BodyImpl.extractContentAsDocument(BodyImpl.java:338) ~[saaj-impl-1.5.3.jar:1.5.3]
    at com.lbg.rsk.cdp.core.controller.WrapperController.wrapSOAPRequest(WrapperController.java:265) ~[main/:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96) ~[spring-boot-actuator-2.6.6.jar:2.6.6]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.60.jar:9.0.60]
    at java.lang.Thread.run(Thread.java:829) [?:?]

问题分析与解决方案

问题根源

extractContentAsDocument()方法尝试将SOAP Body内容转为Document时,出现了TextImpl转Element的类型转换错误,说明SOAP Body的结构不符合预期——可能Body中包含空白文本节点,或者请求的SOAP消息格式不正确,导致解析时无法正确提取到Element节点。

修复方案

  1. 直接解析SOAP Body的子元素
    跳过extractContentAsDocument(),直接遍历SOAP Body的子节点,筛选出实际的请求Element节点再进行JAXB解析:

    @RestController
    public class WrapperController {
    
      @PostMapping(
          value = "/wrap",
          consumes = MediaType.APPLICATION_XML_VALUE,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public Q248Req wrapSOAPRequest(@RequestBody String SOAPReq)
          throws SOAPException, IOException, JAXBException {
        System.out.println("SOAP Request: " + SOAPReq);
        SOAPMessage message =
            MessageFactory.newInstance()
                .createMessage(null, new ByteArrayInputStream(SOAPReq.getBytes(StandardCharsets.UTF_8)));
        // 遍历Body子节点,找到实际的请求元素
        NodeList nodeList = message.getSOAPBody().getChildNodes();
        Element requestElement = null;
        for (int i = 0; i < nodeList.getLength(); i++) {
          Node node = nodeList.item(i);
          if (node.getNodeType() == Node.ELEMENT_NODE) {
            requestElement = (Element) node;
            break;
          }
        }
        if (requestElement == null) {
          throw new IllegalArgumentException("SOAP Body中未找到有效请求元素");
        }
        Unmarshaller unmarshaller = JAXBContext.newInstance(TestObj.class).createUnmarshaller();
        TestObj req = (TestObj) unmarshaller.unmarshal(requestElement);
    
        return req;
      }
    }
    
  2. 校验SOAP请求格式
    确保传入的SOAP请求是标准格式,Body中仅包含目标请求元素,无多余空白或格式错误。标准SOAP请求结构示例:

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://yournamespace.com/test">
       <soapenv:Header/>
       <soapenv:Body>
          <test:YourRequestElement>
             <!-- 请求内容 -->
          </test:YourRequestElement>
       </soapenv:Body>
    </soapenv:Envelope>
    
  3. 使用Spring WS简化SOAP处理
    引入Spring WS框架自动绑定SOAP消息到JAXB生成类,避免手动解析SOAPMessage的繁琐:

    • 添加Spring WS依赖到项目配置文件
    • 创建Endpoint类直接接收解析后的对象:
      @Endpoint
      public class SoapEndpoint {
          private static final String NAMESPACE_URI = "http://yournamespace.com/test";
      
          @PayloadRoot(namespace = NAMESPACE_URI, localPart = "YourRequestElement")
          @ResponsePayload
          public Q248Req handleRequest(@RequestPayload TestObj request) {
              // 直接使用解析后的TestObj调用REST API
              return request;
          }
      }
      

内容的提问来源于stack exchange,提问作者Somnath Mukherjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 11:45:30