WSDL编译生成SOAP请求执行报错,如何调整命名空间声明位置?
问题描述
- 编译WSDL生成的SOAP请求结构合规,但执行时报错,请求内容如下:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"> <SOAP-ENV:Body> <tempuri2:BillInquiry xmlns:tempuri2="http://BillPayment_SOAP"> <ESB_Header> <requestId>327823792977</requestId> <destSysOprName>ATM_BILL_INQ</destSysOprName> <tranTypeCode> </tranTypeCode> <transmissionDateTime>20231005232051</transmissionDateTime> <RRN>327823792977</RRN> <deliveryChannelName>ATM</deliveryChannelName> <tenantId>ZyLA0LlEzox7Nb9yMUMGeQ==</tenantId> </ESB_Header> <billerID>ZONG0002</billerID> <feeFlag>N</feeFlag> <feeAmount>0</feeAmount> <stanID>792977</stanID> <RRN>327823792977</RRN> <consumerNo>11122233344</consumerNo> <channelId>117602</channelId> </tempuri2:BillInquiry> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
- 执行后返回的异常错误:
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><NS1:Fault xmlns:NS1="http://schemas.xmlsoap.org/soap/envelope/"><faultcode>-1</faultcode><faultstring>Exception Ouccured</faultstring></NS1:Fault></soapenv:Body></soapenv:Envelope>HTTP/1.1 500 Internal Server Error^M Content-Type: text/xml;charset=utf-8^M Server: IBM App Connect Enterprise^M Date: Wed, 04 Oct 2023 11:45:25 GMT^M Content-Length: 309^M Connection: close^M ^M <?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><NS1:Fault xmlns:NS1="http://schemas.xmlsoap.org/soap/envelope/"><faultcode>-1</faultcode><faultstring>Exception Ouccured</faultstring></NS1:Fault></soapenv:Body></soapenv:Envelope>
- 将
tempuri2命名空间声明移至<SOAP-ENV:Envelope>标签后,请求可正常执行,调整后的结构:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:tempuri2="http://BillPayment_SOAP"> <SOAP-ENV:Body> <tempuri2:BillInquiry> <ESB_Header> <requestId>327823792977</requestId> <destSysOprName>ATM_BILL_INQ</destSysOprName> <tranTypeCode> </tranTypeCode> <transmissionDateTime>20231005232051</transmissionDateTime> <RRN>327823792977</RRN> <deliveryChannelName>ATM</deliveryChannelName> <tenantId>ZyLA0LlEzox7Nb9yMUMGeQ==</tenantId> </ESB_Header> <billerID>ZONG0002</billerID> <feeFlag>N</feeFlag> <feeAmount>0</feeAmount> <stanID>792977</stanID> <RRN>327823792977</RRN> <consumerNo>11122233344</consumerNo> <channelId>117602</channelId> </tempuri2:BillInquiry> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
- 尝试修改WSDL生成的代理文件引发错误,希望通过代码实现命名空间位置的调整。
解决方案
根据不同技术栈,可通过消息拦截/修改的方式调整命名空间位置:
Java(JAX-WS)
自定义SOAPHandler拦截出站消息,将命名空间从BillInquiry元素移至Envelope节点:
import javax.xml.namespace.QName; import javax.xml.soap.*; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import java.util.Set; public class NamespaceMoveHandler implements SOAPHandler<SOAPMessageContext> { private static final String TEMPURI_NS = "http://BillPayment_SOAP"; private static final String TEMPURI_PREFIX = "tempuri2"; @Override public Set<QName> getHeaders() { return null; } @Override public boolean handleMessage(SOAPMessageContext context) { Boolean isOutbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (isOutbound) { try { SOAPMessage message = context.getMessage(); SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); // 给Envelope添加命名空间声明 envelope.addNamespaceDeclaration(TEMPURI_PREFIX, TEMPURI_NS); // 获取BillInquiry元素并移除自身的命名空间属性 SOAPBody body = envelope.getBody(); SOAPElement billInquiry = (SOAPElement) body.getChildElements(new QName(TEMPURI_NS, "BillInquiry")).next(); billInquiry.removeAttribute("xmlns:" + TEMPURI_PREFIX); message.saveChanges(); } catch (SOAPException e) { e.printStackTrace(); return false; } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { return true; } @Override public void close(MessageContext context) {} }
注册Handler到客户端代理:
YourBillPaymentService service = new YourBillPaymentService(); BillPaymentPort port = service.getBillPaymentPort(); BindingProvider bindingProvider = (BindingProvider) port; List<Handler> handlers = bindingProvider.getBinding().getHandlerChain(); handlers.add(new NamespaceMoveHandler()); bindingProvider.getBinding().setHandlerChain(handlers);
.NET(WCF)
自定义IClientMessageInspector修改出站请求消息:
using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.Xml; public class NamespaceAdjustInspector : IClientMessageInspector { public void AfterReceiveReply(ref Message reply, object correlationState) {} public object BeforeSendRequest(ref Message request, IClientChannel channel) { var buffer = request.CreateBufferedCopy(int.MaxValue); var tempMsg = buffer.CreateMessage(); XmlDocument doc = new XmlDocument(); using (XmlReader reader = tempMsg.GetReaderAtBodyContents()) { doc.Load(reader); } XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); nsMgr.AddNamespace("soapenv", "http://www.w3.org/2003/05/soap-envelope"); nsMgr.AddNamespace("tempuri2", "http://BillPayment_SOAP"); // 给Envelope添加命名空间 XmlElement envelope = doc.DocumentElement; envelope.SetAttribute("xmlns:tempuri2", "http://BillPayment_SOAP"); // 移除BillInquiry元素上的命名空间属性 XmlElement billInquiry = (XmlElement)doc.SelectSingleNode("//tempuri2:BillInquiry", nsMgr); billInquiry.RemoveAttribute("xmlns:tempuri2"); // 重新构建请求消息 using (XmlNodeReader newReader = new XmlNodeReader(doc)) { Message modifiedMsg = Message.CreateMessage(tempMsg.Version, null, newReader); modifiedMsg.Headers.CopyHeadersFrom(request.Headers); modifiedMsg.Properties.CopyProperties(request.Properties); request = modifiedMsg; } buffer.Close(); return null; } } // 注册Inspector的行为类 public class NamespaceAdjustBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) {} public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(new NamespaceAdjustInspector()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {} public void Validate(ServiceEndpoint endpoint) {} }
在客户端调用前添加行为:
var client = new BillPaymentServiceClient(); client.Endpoint.Behaviors.Add(new NamespaceAdjustBehavior()); var result = client.BillInquiry(yourRequestParams);
Python(Zeep)
自定义Zeep插件修改出站XML结构:
from zeep import Plugin from lxml import etree class NamespaceShiftPlugin(Plugin): def egress(self, envelope, http_headers, operation, binding_options): soap_env_ns = "http://www.w3.org/2003/05/soap-envelope" tempuri_ns = "http://BillPayment_SOAP" # 获取Envelope元素并添加命名空间 envelope_elem = envelope.find(f"{{{soap_env_ns}}}Envelope") envelope_elem.set("xmlns:tempuri2", tempuri_ns) # 获取BillInquiry元素并移除自身的命名空间属性 bill_inquiry_elem = envelope.find(f"{{{soap_env_ns}}}Body/{{{tempuri_ns}}}BillInquiry") if bill_inquiry_elem is not None and "xmlns:tempuri2" in bill_inquiry_elem.attrib: del bill_inquiry_elem.attrib["xmlns:tempuri2"] return envelope, http_headers
创建客户端时加载插件:
from zeep import Client client = Client("your_wsdl_file.wsdl", plugins=[NamespaceShiftPlugin()]) client.service.BillInquiry( ESB_Header={...}, billerID="ZONG0002", feeFlag="N", # 其他参数 )
内容的提问来源于stack exchange,提问作者Hadia-Ali
相关产品推荐
相关产品推荐

