使用spring ws在soap请求中发送Xml数据

时间:2014-04-15 20:29:57

标签: java soap spring-ws

我要求XmlData中的CDATA发送给肥皂请求,该请求会返回附件。

使用org.springframework.oxm.jaxb.Jaxb2Marshaller作为marshaller和unmarshaller。

<![CDATA[<tag>Test</tag>]]>这是我想要在soap请求中发送的内容,远程服务期望它以相同的方式显示。

我创建了数据但是当我使用webServiceTemplate.marshalSendAndReceive(payloadRequest, SoapActionCallback)

以某种方式,有效负载请求xmltags被编码并显示为

&lt;![CDATA[&lt;tag&gt;Test&lt;\tag&gt;

由于此编码,远程服务无法处理请求并发送 org.springframework.ws.soap.client.SoapFaultClientException: Object reference not set to an instance of an object.

我该如何解决这个问题?任何建议!

更新:是否将spring mvc的defaultHtmlEscape作为web.xml中的context-param负责此行为?

2 个答案:

答案 0 :(得分:3)

我在工作的这一天遇到了同样的问题,我们必须在定义到从银行Web服务公开的WSDL中的数据结构的字段文本上注入CDATA部分。

要求是引入CDATA部分以逃避Unicode字符&#34; +&#34;进入一个电话领域。 WSDL不可能因各种动机而改变。

我们有相同的环境:

 org.springframework.oxm.jaxb.Jaxb2Marshaller

webServiceTemplate.marshalSendAndReceive(payloadRequest, SoapActionCallback)

和相同的失败结果

&lt;![CDATA[&lt;tag&gt;+&lt;\tag&gt;

而不是

<[[CDATA[+]]>

经过多次测试以及在Stackoverflow和其他站点上查询了很多字体之后,我们已经了解到JAXB引擎本身不支持该解决方案,并且必须将SOAP消息操作到一个简单的步骤处理请求。

其他解决方案表示使用第三方插件和库,例如MOXy,以完成对CDATA部分的强大支持,也是JAXB。但是,这种解决方案不可能尽快在大型企业应用程序中快速集成。

我在此回复中发布的此解决方案允许使用经典DOMSource对象的规范方法注入CDATASection,该对象从messagecontext转换消息SOAP。

在你的回答中,你公开了解决方案的一部分,这是marshalSendAndReceive的回调。

如果您定义marshalSendAndReceive方法支持的临时回调,并且在此回调中操作注入CDATASection的DOMSource,您可以解决问题。

这是代码:

WebServiceMessageCallback callbackCDATANumTelefono = new WebServiceMessageCallback() {
           public void doWithMessage(WebServiceMessage message) {

                //Recover the DOMSource dal message soap
                DOMSource domSource = (DOMSource)message.getPayloadSource();

                //recover set of child nodes of domsource
                NodeList nodeList = domSource.getNode().getChildNodes();

                //definisco il nome del tag da cercare
                String nameNumTel = "ns2:sNumTel"; //in this example, but you can define another QName string to recover the target node (or nodes)
                Node nodeNumTel = null;
                for (int i = 0; i < nodeList.getLength(); i++) {
                    //search of text node of this tag name
                    if (nodeList.item(i).getNodeName().compareTo(nameNumTel) == 0) {
                        nodeNumTel = nodeList.item(i);
                        break;
                    }
                }
                //recover the string value (in this case of telephone number)
                String valueNumTelefono = nodeNumTel.getTextContent();
                //clean of value of target text node
                nodeNumTel.setTextContent("");
                //define and inject of CDATA section, in this case encapsulate "+" character
                CDATASection cdata = nodeNumTel.getOwnerDocument().createCDATASection("+");
                //create of new text node
                Text tnode = nodeNumTel.getOwnerDocument().createTextNode(valueNumTelefono);
                //append of CDATASection (in this point is possible to inject many CDATA section on various parts of string (very usefull)
                nodeNumTel.appendChild(cdata);
                nodeNumTel.appendChild(tnode);

                //done!
            }
        };

此回调将从marshalSendAndReceive

调用
PayloadSoapResponse output = (PayloadSoapResponse ) getWebServiceTemplate()
                .marshalSendAndReceive(input, callbackCDATANumTelefono);

结果是正确发送请求,CDATA部分有效且未以ascii字符转换。

此解决方案的目标是使用JAXB技术在Spring基础架构上操作CDATA部分,而不是使用第三部分库。 一般来说,回调方法的指令集非常可靠,并且可以在Web服务soap的所有环境中进行注入。关键的方面是使用规范方法将SoapMessage转换为更友好的DOMSource以探索节点。它也可以使用XPath引擎来导航这个DOMSource。

祝你好运!

上午

答案 1 :(得分:2)

使用spring-ws生成SOAP Web服务时遇到了这个问题。 CDATA部分将始终如上所述进行转义。

在我的情况下,解决方案(非常类似于Alessandro)是创建一个EndpointInterceptor,我将所需的节点转换为handleResponse中的org.w3c.dom.CDATASection。 然后,您需要将此拦截器添加到实现中的拦截器列表中。

import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.w3c.dom.CDATASection;

import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import java.util.Iterator;

public class MyCdataInterceptor implements EndpointInterceptor {

@Override
public boolean handleRequest(MessageContext messageContext, Object o) throws Exception {
    return true;
}

@Override
public boolean handleResponse(MessageContext messageContext, Object o) throws Exception {

    WebServiceMessage response = messageContext.getResponse();

    SaajSoapMessage saajSoapMessage = (SaajSoapMessage) response;
    SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
    SOAPPart soapPart = soapMessage.getSOAPPart();
    SOAPEnvelope envelope = soapPart.getEnvelope();
    SOAPBody body = envelope.getBody();
    Iterator it = body.getChildElements();
    ... 
     /* find node of interest */

    Node interestingNode = (Node) blah.getNextSibling();

    CDATASection cdat = soapPart.createCDATASection(interestingNode.getFirstChild().getNodeValue());
    interestingNode.removeChild(interestingNode.getFirstChild());
    interestingNode.appendChild(cdat);
    return true;
}

@Override
public boolean handleFault(MessageContext messageContext, Object o) throws Exception {
    return true;
}

@Override
public void afterCompletion(MessageContext messageContext, Object o, Exception e) throws Exception {

}
}