使用旧 SOAP 服务时如何创建自定义 XML 命名空间属性?

编程入门 行业动态 更新时间:2024-10-26 00:22:23
本文介绍了使用旧 SOAP 服务时如何创建自定义 XML 命名空间属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我有一个旧的 Tibco SOAP 服务,我需要从中获取一些数据.不幸的是,该服务非常注重请求消息上的 XML 命名空间属性.我在使用 PeopleSoft 的服务时也遇到过这类问题(en.wikipedia/wiki/PeopleCode).

我从服务中获取了 .wsdl 并创建了一个服务引用.

开箱即用,.Net 产生的 XML 请求消息是:

<s:Body xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:xsd="www.w3/2001/XMLSchema"><getLocation xmlns="customNamespaceHere"><上下文><source>SysAdmin</source></上下文><address1>123 Main St</address1><city>纽约</city><state>纽约</state><国家>美国</国家></地址></getLocation></s:身体></s:信封>

真正有效的是(我使用 SoapUI 发现了这一点):

<s:身体><getLocation xmlns="customNamespaceHere"><上下文><source>SysAdmin</source></上下文><address1>123 Main St</address1><city>纽约</city><state>纽约</state><国家>美国</国家></地址></getLocation></s:身体></s:信封>

注意 body 标签中没有 xsi 和 xsd 前缀.

问:我怎样才能让 .Net 发送正确的 XML,而无需手动滚动 XML 文档并手动将其发送到服务?

解决方案

答:通过实现 IClientMessageInspector,我能够在发送之前修改 XML 请求.

首先,我必须通过实现 IClientMessageInspector 来扩展 WCF.这使我能够访问请求对象并修改 XML 请求消息.这些类不需要位于您的解决方案中的任何特定位置(据我所知).

公共类 ExtendedClientMessageInspector : IClientMessageInspector{//在这里,我们可以在发送之前更改 xml 请求正文.公共对象 BeforeSendRequest(ref Message request, IClientChannel channel){return TibcoService.ModifyGetLocationRequest(ref request);}//结尾public void AfterReceiveReply(ref 消息回复,对象相关状态){返回;}//结尾}//结束类公共类 ExtendedEndpointBehavior : IEndpointBehavior{公共无效AddBindingParameters(ServiceEndpoint端点,BindingParameterCollection bindingParameters){返回;}//结尾public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime){clientRuntime.MessageInspectors.Add(new ExtendedClientMessageInspector());}//结尾public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher){返回;}//结尾公共无效验证(ServiceEndpoint 端点){返回;}//结尾}//结束类公共类 EndpointBehaviorExtensionElement : BehaviorExtensionElement{受保护的覆盖对象 CreateBehavior(){返回新的扩展端点行为();}//结尾公共覆盖类型行为类型{得到{返回 typeof(ExtendedEndpointBehavior);}}//结尾}//结束类

我确实将这个服务中专门处理XML的方法与服务放在同一个类中:

public static Message ModifyGetLocationRequest(ref Message request){//初始化对象var xmlDocument = new XmlDocument();var memoryStream = new MemoryStream();var xmlWriter = XmlWriter.Create(memoryStream);var xmlAttribute = xmlDocument.CreateAttribute("xmlns", "api", "www.w3/2000/xmlns/");xmlAttribute.Value = "customNamespaceHere";//将xml请求消息写入内存流request.WriteMessage(xmlWriter);//清除 xmlWriterxmlWriter.Flush();//把memoryStream中的指针放到开头memoryStream.Position = 0;//将内存流加载到 xmlDocument 中xmlDocument.Load(memoryStream);//从顶部向下删除第二个节点的属性xmlDocument.ChildNodes[1].ChildNodes[1].Attributes.RemoveAll();//重置 memoryStream 对象 - 本质上是将它清空memoryStream.SetLength(0);//重新初始化 xmlWriterxmlWriter = XmlWriter.Create(memoryStream);//将修改后的xml请求消息(xmlDocument)写入xmlWriter中的memoryStreamxmlDocument.WriteTo(xmlWriter);//清除 xmlWriterxmlWriter.Flush();//把memoryStream中的指针放到开头memoryStream.Position = 0;//使用包含 xmlDocument 的 memoryStream 创建一个新的 xmlReadervar xmlReader = XmlReader.Create(memoryStream);//使用修改后的 xmlDocument 创建一个新的请求消息request = Message.CreateMessage(xmlReader, int.MaxValue, request.Version);退货请求;}//结尾

要在调用服务时使这一切正常工作,您需要修改您的 web.config 或 app.config.在您的端点声明中,您需要指定一个 behaviorConfiguration 元素,如下所示:

<端点地址="1.2.3.4:1234/InventoryBinding"binding="basicHttpBinding" bindingConfiguration="HttpSoapBinding"合同="TibcoSvc.InventoryPort" name="InventoryPort"行为配置=clientInspectorsAdded"/></客户端>

您还需要指定扩展名和行为:

<行为扩展><add name="ExtendedEndpointBehavior" type="Sample.Integrations.EndpointBehaviorExtensionElement, Sample.Integrations, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"/></behaviorExtensions></扩展名><行为><端点行为><行为名称="clientInspectorsAdded"><扩展端点行为/></行为></endpointBehaviors></行为></system.serviceModel>

请注意,扩展名必须与返回的名称完全一致:

var foo = typeof(.EndpointBehaviorExtensionElement).AssemblyQualifiedName;

这里有几个我觉得有用的链接:

social.msdn.microsoft/Forums/vstudio/en-US/01440583-d406-4426-8667-63c6eda431fa/remove-xmlnsxsi-and-xmlnsxsd-from-soap-request-body-tag-aspnet?forum=wcf

social.msdn.microsoft/Forums/vstudio/en-US/51547537-fdae-4837-9bd1-30e445d378e9/removing-xmlnsxsihttpwwww3org2001xmlschemainstance-and?forum>

weblogs.asp/paolopia/writing-a-wcf-message-inspector

/msdn.microsoft/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector(v=vs.100).aspx

I have a legacy Tibco SOAP service that I need to get some data from. Unfortunately this service is very particular about the XML namespace attributes on the request message. I have also run into this sort of problem when consuming services from PeopleSoft (en.wikipedia/wiki/PeopleCode).

I got the .wsdl from the service and created a service reference.

Out of the box, the XML request message that .Net produces is:

<s:Envelope xmlns:s="schemas.xmlsoap/soap/envelope/"> <s:Body xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:xsd="www.w3/2001/XMLSchema"> <getLocation xmlns="customNamespaceHere"> <context> <source>SysAdmin</source> </context> <Address> <address1>123 Main St</address1> <city>New York</city> <state>NY</state> <country>US</country> </Address> </getLocation> </s:Body> </s:Envelope>

What actually works is (I figured this out using SoapUI):

<s:Envelope xmlns:s="schemas.xmlsoap/soap/envelope/"> <s:Body> <getLocation xmlns="customNamespaceHere"> <context> <source>SysAdmin</source> </context> <Address> <address1>123 Main St</address1> <city>New York</city> <state>NY</state> <country>US</country> </Address> </getLocation> </s:Body> </s:Envelope>

Notice the absence of the xsi and xsd prefixes within the body tag.

Q: How can I get .Net to send the correct XML short of hand rolling the XML document and manually sending it to the service?

解决方案

A: I was able to modify the XML request before sending it out by implementing IClientMessageInspector.

First I had to extend WCF by implementing IClientMessageInspector. This allowed me to gain access to the request object and modify the XML request message. These classes don't need to be in any particular location within you solution (as far as I know).

public class ExtendedClientMessageInspector : IClientMessageInspector { // Here we can alter the xml request body before it gets sent out. public object BeforeSendRequest(ref Message request, IClientChannel channel) { return TibcoService.ModifyGetLocationRequest(ref request); } // end public void AfterReceiveReply(ref Message reply, object correlationState) { return; } //end } // end class public class ExtendedEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { return; } // end public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new ExtendedClientMessageInspector()); } // end public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { return; } // end public void Validate(ServiceEndpoint endpoint) { return; } // end } // end class public class EndpointBehaviorExtensionElement : BehaviorExtensionElement { protected override object CreateBehavior() { return new ExtendedEndpointBehavior(); } // end public override Type BehaviorType { get { return typeof(ExtendedEndpointBehavior); } } // end } // end class

I did put the method that specifically deals with the XML in this service in the same class as the service:

public static Message ModifyGetLocationRequest(ref Message request) { // Initialize objects var xmlDocument = new XmlDocument(); var memoryStream = new MemoryStream(); var xmlWriter = XmlWriter.Create(memoryStream); var xmlAttribute = xmlDocument.CreateAttribute("xmlns", "api", "www.w3/2000/xmlns/"); xmlAttribute.Value = "customNamespaceHere"; // Write the xml request message into the memory stream request.WriteMessage(xmlWriter); // Clear the xmlWriter xmlWriter.Flush(); // Place the pointer in the memoryStream to the beginning memoryStream.Position = 0; // Load the memory stream into the xmlDocument xmlDocument.Load(memoryStream); // Remove the attributes from the second node down form the top xmlDocument.ChildNodes[1].ChildNodes[1].Attributes.RemoveAll(); // Reset the memoryStream object - essentially nulls it out memoryStream.SetLength(0); // ReInitialize the xmlWriter xmlWriter = XmlWriter.Create(memoryStream); // Write the modified xml request message (xmlDocument) to the memoryStream in the xmlWriter xmlDocument.WriteTo(xmlWriter); // Clear the xmlWriter xmlWriter.Flush(); // Place the pointer in the memoryStream to the beginning memoryStream.Position = 0; // Create a new xmlReader with the memoryStream that contains the xmlDocument var xmlReader = XmlReader.Create(memoryStream); // Create a new request message with the modified xmlDocument request = Message.CreateMessage(xmlReader, int.MaxValue, request.Version); return request; } // end

To get this all to work when the service is called you will need to modify your web.config or app.config. On your endpoint declaration you will need to specify a behaviorConfiguration element like so:

<client> <endpoint address="1.2.3.4:1234/InventoryBinding" binding="basicHttpBinding" bindingConfiguration="HttpSoapBinding" contract="TibcoSvc.InventoryPort" name="InventoryPort" behaviorConfiguration="clientInspectorsAdded" /> </client>

You will also need to specify the extension and the behavior as well:

<system.serviceModel> <extensions> <behaviorExtensions> <add name="ExtendedEndpointBehavior" type="Sample.Integrations.EndpointBehaviorExtensionElement, Sample.Integrations, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="clientInspectorsAdded"> <ExtendedEndpointBehavior /> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel>

Note here that the extension name needs to be exactly what is returned by:

var foo = typeof(<PutYourNamespaceHereNoAngleBrackets>.EndpointBehaviorExtensionElement).AssemblyQualifiedName;

Here are a few links that I found helpful:

social.msdn.microsoft/Forums/vstudio/en-US/01440583-d406-4426-8667-63c6eda431fa/remove-xmlnsxsi-and-xmlnsxsd-from-soap-request-body-tag-aspnet?forum=wcf

social.msdn.microsoft/Forums/vstudio/en-US/51547537-fdae-4837-9bd1-30e445d378e9/removing-xmlnsxsihttpwwww3org2001xmlschemainstance-and?forum=wcf

weblogs.asp/paolopia/writing-a-wcf-message-inspector

msdn.microsoft/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector(v=vs.100).aspx

更多推荐

使用旧 SOAP 服务时如何创建自定义 XML 命名空间属性?

本文发布于:2023-11-05 19:20:07,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1561634.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   属性   空间   SOAP   XML

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!