使用 Spring Boot 和 JAXB 的 SOAP 请求命名空间前缀和结构不匹配

问题描述 投票:0回答:1

我正在开发一个 Spring Boot 项目,我需要使用 SOAP Web 服务。我使用

wsdl2java
生成客户端代码。但是,我遇到了生成的 SOAP 请求消息结构的问题。

这是来自 SOAP-UI 的预期 SOAP 请求:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:uri="http://uri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <uri:GetPolicy>
         <uri:policyId>12345</uri:policyId>
      </uri:GetPolicy>
   </soapenv:Body>
</soapenv:Envelope>

但是,我的 Spring Boot 应用程序生成以下 SOAP 请求:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns3:GetPolicy xmlns:ns2="http://schemas.data.org/PolicyServicesLibrary" xmlns:ns3="http://uri.org/" xmlns:ns4="http://schemas.microsoft.com/Serialization/Arrays" xmlns:ns5="http://schemas.microsoft.com/Serialization/">
            <ns3:policyId>12345</ns3:policyId>
        </ns3:GetPolicy>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

差异包括如下,当我尝试时,由于请求 SOAP 负载不正确而收到错误。

  1. 命名空间前缀(
    soapenv
    vs
    SOAP-ENV
    )。
  2. 额外不必要的命名空间。

这是我的

GetPolicy
课程:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "policyId"
})
@XmlRootElement(name = "GetPolicy")
public class GetPolicy {

    @XmlElementRef(name = "policyId", namespace = "http://uri.org/", type = JAXBElement.class, required = false)
    protected JAXBElement<String> policyId;

    public JAXBElement<String> getPolicyId() {
        return policyId;
    }

    public void setPolicyId(JAXBElement<String> value) {
        this.policyId = value;
    }
}

并且,这是我执行请求的配置和代码:

@Autowired
private WebServiceTemplate webServiceTemplate;

ObjectFactory factory = new ObjectFactory();
GetPolicy request = factory.createGetPolicy();
request.setPolicyId(factory.createGetPolicyPolicyId("12345"));

webServiceTemplate.setMarshaller(jaxb2Marshaller);
webServiceTemplate.setUnmarshaller(jaxb2Marshaller);
webServiceTemplate.setDefaultUri("SOAP_SERVER_URL");

JAXBElement<GetPolicyResponse> response = (JAXBElement<GetPolicyResponse>) webServiceTemplate.marshalSendAndReceive(request, new SoapActionCallback("SOAP_ACTION"));
return response.getValue();

我还确保了

package-info.java
中正确的包级命名空间定义:

@jakarta.xml.bind.annotation.XmlSchema(
    namespace = "http://uri.org/",
    elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED
)
package my.package.name;

尽管有这些配置,生成的 SOAP 请求仍具有不正确的命名空间前缀,并包含其他命名空间。另外,我知道通过添加

XmlNs
我可以解决前缀问题,但我有多个
namespaceURI
应该与相同的
prefix
匹配。这就是我坚持的地方。

问题:

  1. 如何确保生成的 SOAP 请求使用
    soapenv
    作为命名空间前缀而不是
    SOAP-ENV
  2. 如何防止在文件中包含不必要的命名空间 生成 SOAP 请求?

谢谢你。任何帮助将不胜感激!

java spring spring-boot web-services soap
1个回答
0
投票

对于您的第一个问题,更改您需要实现 ClientInterceptor 的名称空间前缀(SOAP-ENV 到soapenv)并将其设置为

WebServiceTemplate

import jakarta.xml.soap.*;
import lombok.SneakyThrows;
import org.springframework.ws.client.WebServiceClientException;
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.saaj.SaajSoapMessage;

public class Interceptor implements ClientInterceptor {

    @SneakyThrows
    public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
        changePrefix(messageContext);
        return true;
    }

    public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
        // if you need to change in the response call the method 'changePrefix' here
        return true;
    }

    public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
        // if you need to change in the fault response call the method 'changePrefix' here
        return true;
    }

    public void afterCompletion(MessageContext messageContext, Exception e) throws WebServiceClientException {

    }

    private void changePrefix(MessageContext messageContext) throws SOAPException {
        SaajSoapMessage soapMessage = (SaajSoapMessage) messageContext.getRequest();

        SOAPEnvelope soapEnvelope = soapMessage.getSaajMessage().getSOAPPart().getEnvelope();
        SOAPHeader soapHeader = soapEnvelope.getHeader();
        SOAPBody soapBody = soapEnvelope.getBody();
        SOAPFault soapFault = soapBody.getFault();

        soapEnvelope.removeNamespaceDeclaration(soapEnvelope.getPrefix());
        soapEnvelope.addNamespaceDeclaration("soapenv", soapEnvelope.getNamespaceURI());
        soapEnvelope.setPrefix("soapenv");

        if (soapHeader != null) soapHeader.setPrefix("soapenv");
        if (soapBody != null) soapBody.setPrefix("soapenv");
        if (soapFault != null) soapFault.setPrefix("soapenv");
    }
}

要将其设置为

webServiceTemplate
,您可以创建一个Bean或任何您喜欢的方法,将创建的拦截器添加到
new ClientInterceptor[]
数组中,

webServiceTemplate.setInterceptors(new ClientInterceptor[]{interceptor});

对于第二个问题,是的,当您需要更改 Soap Body 中的其他前缀为

package-info.java
时,您可以使用
XmlNs
并添加
namespaceURI

© www.soinside.com 2019 - 2024. All rights reserved.