在 WSDL 中从具有自定义绑定的 WCF 终结点返回的 SOAP 版本与配置不匹配

本文关键字:SOAP 返回 结点 版本 不匹配 配置 WCF 自定义 绑定 WSDL | 更新日期: 2023-09-27 18:34:04

我们有一个现有的 WCF 终结点,该终结点当前使用具有默认 SOAP 1.1 绑定的basicHttpBinding。它由我们控制的.NET Compact Framework 3.5客户端应用程序调用。我们需要一些自定义日志记录,包括请求和响应的线路上的字节数。为了获得这一点,我正在开发一个自定义的MessageEncoder和IDispatchMessageInspector,并在配置中使用相关的自定义绑定。这一切都工作正常,客户端能够使用在我进行更改之前生成的代理类毫无问题地调用端点。我遇到的问题是,当我尝试使用 NetCFSvcUtil.exe 重新生成代理类时,它失败了,因为 WSDL 说端点正在使用 SOAP 1.2。

错误:.NET Compact Framework 不支持此服务提供的任何绑定。

CustomBinding_IAssetService:.NET Compact Framework 不支持绑定元素选项 TextMessageEncodingBindingElement.MessageVersion = Soap12 (http://www.w3.org/2003/05/soap-envelope( AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none(。

因此,我使用浏览器查看 WSDL,它将端点报告为 SOAP 1.2。

为我的自定义绑定生成的 WSDL:

<wsdl:binding name="CustomBinding_IAssetService" type="tns:IAssetService">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>

使用旧绑定生成的 WSDL:

<wsdl:binding name="BasicHttpBinding_IAssetService" type="tns:IAssetService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>

有趣的是,如果我将自定义绑定配置中的自定义编码器替换为 <textMessageEncoding messageVersion="Soap11"> 元素,WSDL 将包含预期的 SOAP 1.1 绑定:

<wsdl:binding name="CustomBinding_IAssetService" type="tns:IAssetService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>

这让我相信这是我的自定义类中的一个问题。我调试了代码以查看从内部TextMessageEncodingBindingElement对象和我的自定义绑定类(ThroughputEncoderBindingElementThroughputEncoderExtensionElementThroughputEncoderFactoryThroughputEncoder(返回的MessageVersion的值,并且在发出 WSDL 请求的所有情况下都是 SOAP 1.1。

我的代码或配置中是否有任何问题?还有什么我可以尝试的吗?

原始装订

<basicHttpBinding>
    <binding name="HttpBinding" maxReceivedMessageSize="2147483647">
        <readerQuotas maxDepth="2147483647" ... />
    </binding>
</basicHttpBinding>

服务的原始配置

<service name="Company.AssetService" behaviorConfiguration="DefaultServiceBehavior">
    <host>
        <baseAddresses>
            <add baseAddress="http://localhost:8000/Asset/service"/>
        </baseAddresses>
    </host>
    <endpoint address=""
              binding="wsHttpBinding"
              bindingConfiguration="WsBinding"
              contract="Company.IAssetService" />
    <endpoint address="basic"
              binding="basicHttpBinding"
              bindingConfiguration="HttpBinding"
              contract="Company.IAssetService" />
</service>

新绑定

<customBinding>
    <binding name="throughputEncoding" maxReceivedMessageSize="2147483647">
      <throughputEncoding>
        <textEncoding messageVersion="Soap11">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" ... />
        </textEncoding>
      </throughputEncoding>
      <httpTransport/>
    </binding>
</customBinding>

服务的新配置

<service name="Company.AssetService" behaviorConfiguration="DefaultServiceBehavior">
    <host>
        <baseAddresses>
            <add baseAddress="http://localhost:8000/Asset/service"/>
        </baseAddresses>
    </host>
    <endpoint address=""
              binding="wsHttpBinding"
              bindingConfiguration="WsBinding"
              contract="Company.IAssetService" />
    <endpoint address="basic"
              binding="customBinding"
              bindingConfiguration="throughputEncoding"
              contract="Company.IAssetService" />
</service>

扩展配置

<extensions>
    ...
  <bindingElementExtensions>
    <add name="throughputEncoding"
         type="Company.Infrastructure.ThroughputEncoderExtensionElement, Company, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </bindingElementExtensions>
</extensions>

public class ThroughputEncoder : MessageEncoder
{
    private readonly MessageEncoder innerEncoder;
    private readonly WcfMessageLogger logger;
    public ThroughputEncoder(MessageEncoder innerEncoder)
    {
        this.innerEncoder = innerEncoder;
        logger = new WcfMessageLogger();
    }
    public override string ContentType
    {
        get { return "text/xml"; }
    }
    public override string MediaType
    {
        get { return "text/xml"; }
    }
    public override MessageVersion MessageVersion
    {
        get { return innerEncoder.MessageVersion; }
    }
    ...
}
public class ThroughputEncoderFactory : MessageEncoderFactory
{
    private readonly ThroughputEncoder throughputEncoder;
    public ThroughputEncoderFactory(MessageEncoder messageEncoder)
    {
        throughputEncoder = new ThroughputEncoder(messageEncoder);
    }
    public override MessageEncoder Encoder
    {
        get { return throughputEncoder; }
    }
    public override MessageVersion MessageVersion
    {
        get { return throughputEncoder.MessageVersion; }
    }
}
public class ThroughputEncoderExtensionElement : BindingElementExtensionElement
{
    private const string TextEncodingPropertyName = "textEncoding";
    [ConfigurationProperty(TextEncodingPropertyName)]
    public TextMessageEncodingElement TextEncoding
    {
        get { return (TextMessageEncodingElement)this[TextEncodingPropertyName]; }
        set { this[TextEncodingPropertyName] = value; }
    }
    protected override BindingElement CreateBindingElement()
    {
        var textBindingElement = new TextMessageEncodingBindingElement();
        if (TextEncoding != null)
            TextEncoding.ApplyConfiguration(textBindingElement);
        return new ThroughputEncoderBindingElement(textBindingElement);
    }
    public override Type BindingElementType
    {
        get { return typeof (ThroughputEncoderBindingElement); }
    }
}
public class ThroughputEncoderBindingElement : MessageEncodingBindingElement
{
    private readonly MessageEncodingBindingElement innerBindingElement;
    public ThroughputEncoderBindingElement(MessageEncodingBindingElement innerBindingElement)
    {
        this.innerBindingElement = innerBindingElement;
    }
    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(this);
        return base.BuildChannelFactory<TChannel>(context);
    }
    public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(this);
        return base.BuildChannelListener<TChannel>(context);
    }
    public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(this);
        return base.CanBuildChannelFactory<TChannel>(context);
    }
    public override bool CanBuildChannelListener<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(this);
        return base.CanBuildChannelListener<TChannel>(context);
    }
    public override MessageEncoderFactory CreateMessageEncoderFactory()
    {
        var innerFactory = innerBindingElement.CreateMessageEncoderFactory();
        return new ThroughputEncoderFactory(innerFactory.Encoder);
    }
    public override BindingElement Clone()
    {
        return new ThroughputEncoderBindingElement(innerBindingElement);
    }
    public override T GetProperty<T>(BindingContext context)
    {
        return innerBindingElement.GetProperty<T>(context) ?? context.GetInnerProperty<T>();
    }
    public override MessageVersion MessageVersion
    {
        get { return innerBindingElement.MessageVersion; }
        set { innerBindingElement.MessageVersion = value; }
    }
}

在 WSDL 中从具有自定义绑定的 WCF 终结点返回的 SOAP 版本与配置不匹配

您需要在 BindingElement 上实现 IWsdlExportExtension 接口。

有了它,您可以通过实现 ExportContractExportEndpoint 函数来告诉 WCF 它应该如何为您的服务生成 WSDL。

您可以在Microsoft的 CustomTextMesssageEncoder WCF 示例中查看它是如何完成的。

请参见:https://msdn.microsoft.com/en-us/library/ms751486(v=vs.110(.aspx

下载示例,即可获得它。