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

时间:2015-03-30 17:01:58

标签: c# xml wcf soap

我有一个传统的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文档并手动将其发送到服务?

1 个答案:

答案 0 :(得分:1)

答:我可以通过实现IClientMessageInspector在发送之前修改XML请求。

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

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有关的方法放在与服务相同的类中:

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; 

以下是我发现的一些有用的链接:

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

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

http://weblogs.asp.net/paolopia/writing-a-wcf-message-inspector

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