使用来自 Spring-ws 的 WebServiceTemplate 生成空命名空间前缀

Generation of empty namespace prefix using WebServiceTemplate from Spring-ws

我们正在使用 Spring-ws 版本 2.4。2.RELEASE 从 WSDL 调用外部 WebService。

我们调用的方法是:

ValidateObjectResponse response = (ValidateObjectResponse) webServiceTemplate.marshalSendAndReceive(request);

生成的 SOAP 请求包含一个空的命名空间前缀:

<SOAP-ENV:Envelope xmlns:SOAPENV="http://schemas.xmlsoap.org/soap/envelope/"> 
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns2:validateObject xmlns:ns2="http://ws.validator.sch.gazelle.ihe.net/" xmlns=""><base64ObjectToValidate>Cgo8P3ht...
    </SOAP-ENV:Body>
</SOAP-ENV>

在幕后,使用了 apache axiom(版本 1.2.13),在发行说明中我阅读了以下内容(https://ws.apache.org/axiom/release-notes/1.2.13.html):

"In Axiom 1.2.12, the declareNamespace methods in OMElement didn’t enforce this constraint and namespace declarations violating this requirement were silently dropped during serialization. This behavior is problematic because it may result in subtle issues such as unbound namespace prefixes. In Axiom 1.2.13 these methods have been changed so that they throw an exception if an attempt is made to bind the empty namespace name to a prefix."

当我们调用外部网络服务时,出现以下异常:

java.lang.IllegalArgumentException: Cannot bind a prefix to the empty namespace name
at org.apache.axiom.om.impl.dom.ElementImpl.declareNamespace(ElementImpl.java:754)
at org.apache.axiom.om.impl.dom.ElementImpl.declareNamespace(ElementImpl.java:778)
at org.apache.axiom.om.impl.dom.ElementImpl.setAttributeNS(ElementImpl.java:559)
at com.sun.xml.bind.marshaller.SAX2DOMEx.startElement(SAX2DOMEx.java:163)
at com.sun.xml.bind.v2.runtime.output.SAXOutput.endStartTag(SAXOutput.java:124)
at com.sun.xml.bind.v2.runtime.XMLSerializer.endAttributes(XMLSerializer.java:302)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:588)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:312)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:490)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:328)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:257)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.marshal(Jaxb2Marshaller.java:680)
at org.springframework.ws.support.MarshallingUtils.marshal(MarshallingUtils.java:81)
at org.springframework.ws.client.core.WebServiceTemplate.doWithMessage(WebServiceTemplate.java:399)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:590)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:373)

我的第一个想法是把axiom的版本降到2.1.12,但是这样会和其他库产生冲突。

有没有办法避免使用 spring-ws 创建空的命名空间前缀?

我们有一个 WSDL,在我看来一切正常,但我们无法修改它:

<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://ws.validator.sch.gazelle.ihe.net/"
              xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
              name="GazelleObjectValidatorService" targetNamespace="http://ws.validator.sch.gazelle.ihe.net/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.validator.sch.gazelle.ihe.net/"
               attributeFormDefault="unqualified" elementFormDefault="unqualified"
               targetNamespace="http://ws.validator.sch.gazelle.ihe.net/">
        <xs:element name="about" type="tns:about"/>
        <xs:element name="aboutResponse" type="tns:aboutResponse"/>
        <xs:element name="getAllAvailableObjectTypes" type="tns:getAllAvailableObjectTypes"/>
        <xs:element name="getAllAvailableObjectTypesResponse" type="tns:getAllAvailableObjectTypesResponse"/>
        <xs:element name="getAllSchematrons" type="tns:getAllSchematrons"/>
        <xs:element name="getAllSchematronsResponse" type="tns:getAllSchematronsResponse"/>
        <xs:element name="getSchematronByName" type="tns:getSchematronByName"/>
        <xs:element name="getSchematronByNameResponse" type="tns:getSchematronByNameResponse"/>
        <xs:element name="getSchematronsForAGivenType" type="tns:getSchematronsForAGivenType"/>
        <xs:element name="getSchematronsForAGivenTypeResponse" type="tns:getSchematronsForAGivenTypeResponse"/>
        <xs:element name="validateObject" type="tns:validateObject"/>
        <xs:element name="validateObjectResponse" type="tns:validateObjectResponse"/>

这是我们创建 WebServiceTemplate 的代码:

Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.test");
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageSender(new HttpComponentsMessageSender(httpClientBuilder.build()));

ValidateObject request = new ValidateObject();
request.setBase64ObjectToValidate(base64Object);
request.setXmlReferencedStandard(xmlReferencedStandard);
request.setXmlMetadata(xmlMetadata);
ValidateObjectResponse response = (ValidateObjectResponse) 
webServiceTemplate.marshalSendAndReceive(request);
return response.getValidationResult();

问题是如何避免生成空命名空间名称:

<ns2:validateObject xmlns:ns2="http://ws.validator.sch.gazelle.ihe.net/" xmlns=""> 

让公理不再抱怨?

问题最终是spring-ws使用的axiom jar和Axis使用的axiom jar之间的jar冲突。

通过强制为 WebServiceTemplate 使用特定的 SaajSoapMessageFactory:

SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(new SOAPMessageFactory1_1Impl());
saajSoapMessageFactory.setSoapVersion(SoapVersion.SOAP_11);
saajSoapMessageFactory.afterPropertiesSet();

webServiceTemplate.setMessageFactory(saajSoapMessageFactory);

我们设法解决了我们的问题。

为了生成空命名空间,请尝试为子元素和父元素设置相同的命名空间;应该在没有名称空间的情况下生成子元素。