使用遗留 SOAP 服务时如何创建自定义 XML 名称空间属性?
How can I create custom XML namespace attributes when consuming a legacy SOAP service?
我有一个遗留的 Tibco SOAP 服务,我需要从中获取一些数据。不幸的是,此服务对请求消息上的 XML 名称空间属性非常特别。在使用 PeopleSoft (https://en.wikipedia.org/wiki/PeopleCode) 的服务时,我也 运行 遇到过此类问题。
我从服务中获取了 .wsdl 并创建了一个服务引用。
开箱即用,.Net 生成的 XML 请求消息是:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<getLocation xmlns="http://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>
实际有效的是(我使用 SoapUI 解决了这个问题):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<getLocation xmlns="http://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>
请注意 body 标签中没有 xsi 和 xsd 前缀。
问:如何让 .Net 发送正确的 XML 而不是手动滚动 XML 文档并将其手动发送到服务?
答:通过实现 IClientMessageInspector,我能够在发送之前修改 XML 请求。
首先,我必须通过实现 IClientMessageInspector 来扩展 WCF。这使我能够访问请求对象并修改 XML 请求消息。这些 classes 不需要位于您解决方案中的任何特定位置(据我所知)。
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
我确实把这个服务中专门处理XML的方法和服务放在了同一个class中:
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", "http://www.w3.org/2000/xmlns/");
xmlAttribute.Value = "http://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
要在调用服务时使这一切正常工作,您需要修改 web.config 或 app.config。在端点声明中,您需要指定一个 behaviorConfiguration 元素,如下所示:
<client>
<endpoint address="http://1.2.3.4:1234/InventoryBinding"
binding="basicHttpBinding" bindingConfiguration="HttpSoapBinding"
contract="TibcoSvc.InventoryPort" name="InventoryPort"
behaviorConfiguration="clientInspectorsAdded" />
</client>
您还需要指定扩展名和行为:
<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>
这里注意扩展名需要和返回的完全一样:
var foo = typeof(<PutYourNamespaceHereNoAngleBrackets>.EndpointBehaviorExtensionElement).AssemblyQualifiedName;
以下是一些我认为有用的链接:
http://weblogs.asp.net/paolopia/writing-a-wcf-message-inspector
我有一个遗留的 Tibco SOAP 服务,我需要从中获取一些数据。不幸的是,此服务对请求消息上的 XML 名称空间属性非常特别。在使用 PeopleSoft (https://en.wikipedia.org/wiki/PeopleCode) 的服务时,我也 运行 遇到过此类问题。
我从服务中获取了 .wsdl 并创建了一个服务引用。
开箱即用,.Net 生成的 XML 请求消息是:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<getLocation xmlns="http://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>
实际有效的是(我使用 SoapUI 解决了这个问题):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<getLocation xmlns="http://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>
请注意 body 标签中没有 xsi 和 xsd 前缀。
问:如何让 .Net 发送正确的 XML 而不是手动滚动 XML 文档并将其手动发送到服务?
答:通过实现 IClientMessageInspector,我能够在发送之前修改 XML 请求。
首先,我必须通过实现 IClientMessageInspector 来扩展 WCF。这使我能够访问请求对象并修改 XML 请求消息。这些 classes 不需要位于您解决方案中的任何特定位置(据我所知)。
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
我确实把这个服务中专门处理XML的方法和服务放在了同一个class中:
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", "http://www.w3.org/2000/xmlns/");
xmlAttribute.Value = "http://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
要在调用服务时使这一切正常工作,您需要修改 web.config 或 app.config。在端点声明中,您需要指定一个 behaviorConfiguration 元素,如下所示:
<client>
<endpoint address="http://1.2.3.4:1234/InventoryBinding"
binding="basicHttpBinding" bindingConfiguration="HttpSoapBinding"
contract="TibcoSvc.InventoryPort" name="InventoryPort"
behaviorConfiguration="clientInspectorsAdded" />
</client>
您还需要指定扩展名和行为:
<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>
这里注意扩展名需要和返回的完全一样:
var foo = typeof(<PutYourNamespaceHereNoAngleBrackets>.EndpointBehaviorExtensionElement).AssemblyQualifiedName;
以下是一些我认为有用的链接:
http://weblogs.asp.net/paolopia/writing-a-wcf-message-inspector