【问题标题】:dotnet-svcutil-generated WCF proxy (and even a custom Channel<>) are returning null for a valid SOAP responsedotnet-svcutil 生成的 WCF 代理(甚至自定义 Channel<>)为有效的 SOAP 响应返回 null
【发布时间】:2020-06-05 12:17:49
【问题描述】:

使用 dotnet-svcutil 2.0.1 (dotnet-svcutil --sync --outputDir . http://XXX/?WSDL) 和 System.ServiceModel.* 4.7.0 生成的代理,调用代码和下面的?WSDL。代理根本无法反序列化有效响应,只是返回 null。在 Windows 10 和 macOS Catalina 上尝试了 .NET Core 3.0 和 3.1,结果相同。 Fiddler 请求和响应以及来自服务器的 WSDL(我无法控制的服务器)。

对于代理,我使用@shmao 的set_mode 解决方法(https://github.com/dotnet/wcf/issues/2219) 来避免“不支持JScript/CSharp 脚本”异常。此外,我必须删除 Namespace="" 属性才能使请求部分正常工作。我已经添加了 EventListeners 并从所有事件源中转储了 Verbose 中的所有内容,没有警告/错误,只有空值。

我还尝试了基于 Channel 的 MessageContract/DataContract 方法,最终结果相同为 null (!),我无法利用任何基于 .NET Core WCF 的代码来反序列化给定的响应。

将考虑使用 .NET Core 3.1 WCF 反序列化给定响应的任何解决方案甚至部分解决方案,最好使用 dotnet-svcutil。发现警告/错误甚至手动访问响应字符串仍然是对非 WCF 字符串/基于 HttpRequest 的方法的改进。

      WSWebServiceSoapPortClient proxy;
      try {
        proxy = new WSWebServiceSoapPortClient(new BasicHttpBinding(),
          new EndpointAddress("http://XXX"));

        await proxy.OpenAsync();
      } catch (Exception e) {
        Console.WriteLine(e.Message);
        return;
      }

      if (proxy.State == System.ServiceModel.CommunicationState.Faulted) {
        System.Console.WriteLine("Unable to connect to the proxy.");
        return;
      }

      var one = new WSUserLoginRequest1(new WSUserLoginRequest() {
        userName = "XXX",
        userPassword = "XXX",
      });
      WSUserLoginResponse1 wsUserLoginResponse = null;

      try {
        wsUserLoginResponse = await proxy.WSUserLoginAsync(one);   // returns null
      } catch (Exception e) {
        Console.WriteLine(e.ToString());
        return;
      }

来自服务器的相关 WSDL

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
  xmlns:s="http://www.w3.org/2001/XMLSchema" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
  xmlns:tns="WSWebService" name="WSWebService" targetNamespace="WSWebService">
  <wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="WSWebService">

      ...

      <s:complexType name="WSUserLoginRequest">
        <s:sequence>
          <s:element name="userName" type="s:string" />
          <s:element name="userPassword" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="WSUserLoginResponse">
        <s:sequence>
          <s:element name="userToken" type="s:string" />
          <s:element name="wsdlVersion" type="s:string" minOccurs="1" maxOccurs="1" default="2.0.0.0" />
          <s:element name="result" type="s:int" />
          <s:element name="resultString" type="s:string" />
        </s:sequence>
      </s:complexType>

      ...

      <wsdl:message name="WSUserLoginSoapIn">
        <wsdl:part name="parameters" type="tns:WSUserLoginRequest" />
      </wsdl:message>
      <wsdl:message name="WSUserLoginSoapOut">
        <wsdl:part name="parameters" type="tns:WSUserLoginResponse" />
      </wsdl:message>

      ...

      <wsdl:operation name="WSUserLogin">
        <wsdl:documentation>Authenticate user using provided username and password.</wsdl:documentation>
        <wsdl:input message="tns:WSUserLoginSoapIn" />
        <wsdl:output message="tns:WSUserLoginSoapOut" />
      </wsdl:operation>

(编辑)

WSUserLoginResponse1 的类定义由dotnet-svcutil 2.0.1 生成:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1")]
[System.ServiceModel.MessageContractAttribute(WrapperName = "WSUserLoginResponse", WrapperNamespace = "WSWebService", IsWrapped = true)]
public partial class WSUserLoginResponse1 {

  [System.ServiceModel.MessageBodyMemberAttribute(Namespace = "", Order = 0)]
  public WSUserLoginResponse parameters;

  public WSUserLoginResponse1() {
  }

  public WSUserLoginResponse1(WSUserLoginResponse parameters) {
    this.parameters = parameters;
  }
}

(编辑 2) 来自 dotnet-svcutil 的 WSUserLoginResponse 建议。

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "WSWebService")]
public partial class WSUserLoginResponse {

  private string userTokenField;

  private string wsdlVersionField;

  private int resultField;

  private string resultStringField;

  public WSUserLoginResponse() {
    this.wsdlVersionField = "2.0.0.0";
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]
  public string userToken {
    get {
      return this.userTokenField;
    }
    set {
      this.userTokenField = value;
    }
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 1)]
  public string wsdlVersion {
    get {
      return this.wsdlVersionField;
    }
    set {
      this.wsdlVersionField = value;
    }
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 2)]
  public int result {
    get {
      return this.resultField;
    }
    set {
      this.resultField = value;
    }
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 3)]
  public string resultString {
    get {
      return this.resultStringField;
    }
    set {
      this.resultStringField = value;
    }
  }
}

(编辑 3) 来自 wsdl.exe 的 WSUserLoginResponse 建议。没有 WSUserLoginResponse1

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.6.1087.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="WSWebService")]
    public partial class WSUserLoginResponse : object, System.ComponentModel.INotifyPropertyChanged {

        private string userTokenField;

        private string wsdlVersionField;

        private int resultField;

        private string resultStringField;

        public WSUserLoginResponse() {
            this.wsdlVersionField = "2.0.0.0";
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
        public string userToken {
            get {
                return this.userTokenField;
            }
            set {
                this.userTokenField = value;
                this.RaisePropertyChanged("userToken");
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
        public string wsdlVersion {
            get {
                return this.wsdlVersionField;
            }
            set {
                this.wsdlVersionField = value;
                this.RaisePropertyChanged("wsdlVersion");
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
        public int result {
            get {
                return this.resultField;
            }
            set {
                this.resultField = value;
                this.RaisePropertyChanged("result");
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=3)]
        public string resultString {
            get {
                return this.resultStringField;
            }
            set {
                this.resultStringField = value;
                this.RaisePropertyChanged("resultString");
            }
        }

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }

(编辑 4) 独立 wsdl。

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
  xmlns:s="http://www.w3.org/2001/XMLSchema" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
  xmlns:tns="WSWebService" name="WSWebService" targetNamespace="WSWebService">
  <wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="WSWebService">
      <s:complexType name="WSUserLoginRequest">
        <s:sequence>
          <s:element name="userName" type="s:string" />
          <s:element name="userPassword" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="WSUserLoginResponse">
        <s:sequence>
          <s:element name="userToken" type="s:string" />
          <s:element name="wsdlVersion" type="s:string" minOccurs="1" maxOccurs="1" default="2.0.0.0" />
          <s:element name="result" type="s:int" />
          <s:element name="resultString" type="s:string" />
        </s:sequence>
      </s:complexType>
    </schema>
  </wsdl:types>
  <wsdl:message name="WSUserLoginSoapIn">
    <wsdl:part name="parameters" type="tns:WSUserLoginRequest" />
  </wsdl:message>
  <wsdl:message name="WSUserLoginSoapOut">
    <wsdl:part name="parameters" type="tns:WSUserLoginResponse" />
  </wsdl:message>
  <wsdl:portType name="WSWebServiceSoapPort">
    <wsdl:operation name="WSUserLogin">
      <wsdl:documentation>Documentation</wsdl:documentation>
      <wsdl:input message="tns:WSUserLoginSoapIn" />
      <wsdl:output message="tns:WSUserLoginSoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="WSWebServiceSoapBinding" type="tns:WSWebServiceSoapPort">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="WSUserLogin">
      <soap:operation soapAction="WSUserLogin" style="rpc" />
      <wsdl:input>
        <soap:body use="literal" namespace="WSWebService" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" namespace="WSWebService" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="WSWebService">
    <wsdl:port name="WSWebServiceSoapPort" binding="tns:WSWebServiceSoapBinding">
      <soap:address location="https://x.x.x.x:x" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

【问题讨论】:

  • 可以添加生成的c#定义WSUserLoginResponse1吗?另外,为什么它以1 结尾-WSUserLoginResponse 和 ``WSUserLoginResponse1` 都有吗?
  • @HeyJude,WSUserLoginResponse1定义在dotnet-svcutil生成的Reference.cs中,WSUserLoginAsync返回类型为Task
  • 您没有回答我在评论中提出的问题,所以我想我应该解释一下:我希望您在问题中添加WSUserLoginResponse1 的生成代码,因为它可能没有正确生成.它有时会发生。它以1 结尾的事实有点可疑。
  • 也许您可以使用 Visual Studio 向导生成必要的代码,看看生成的输出是否与 dotnet-svcutil 输出有任何不同? docs.microsoft.com/en-us/dotnet/core/additional-tools/…
  • @bunt,我看到你添加了WSUserLoginResponse1 定义,太好了。现在,由于这个类包含一个名为parameters 的字段,其类型为WSUserLoginResponse,因此还值得添加那个生成的c#定义。

标签: c# wcf soap .net-core wsdl


【解决方案1】:

我使用你的 wsdl 文件生成 c# 代码,使用 dotnet-svcutil 2.0.1 和你一样。

我使用 SoapUI mocking service 模拟了一个 SOAP 端点,将其触发,然后在我的机器上运行您的代码(连接到我的本地主机上的模拟端点)。

我在调用proxy.OpenAsync 时遇到了异常。我收到的异常消息是:

命名空间“”引用中的顶部 XML 元素“参数” 不同的类型 WSUserLoginRequest 和 WSUserLoginResponse。使用 XML 属性为元素指定另一个 XML 名称或命名空间,或 类型。

所以我在生成的代码里面去WSUserLoginRequest1,给parametersNamespace加了一个值,解释here

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1")]
[System.ServiceModel.MessageContractAttribute(WrapperName="WSUserLogin", WrapperNamespace="WSWebService", IsWrapped=true)]
public partial class WSUserLoginRequest1
{
    // replaced Namespace empty string value with my endpoint url
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://localhost:8181/WSUserLogin", Order=0)]
    public WSUserLoginRequest parameters;

    public WSUserLoginRequest1()
    {
    }

    // ...
}

更改之后,它就起作用了,我在致电proxy.WSUserLoginAsync 时得到了回复。

我希望我知道如何通过将值传递给dotnet-svcutil/namespace 选项来添加Namespace 值,但是遵循这些SO 帖子(12,尽管它们与svcutil 有关, 而不是 dotnet-svcutil) 只在生成的文件中的其他地方添加了命名空间。

另外,正如您所指出的,使用wsdl.exe 不会生成不必要的WSUserLoginResponse1,而svcutil-dotnet 会生成。这似乎是一个已知问题,参见例如这里:svcutil generated unneccesary wrapper classes

我希望它对您的问题有任何价值。

【讨论】:

  • 宾果游戏做得很好@HeyJude,它对我有用。在我写“此外,我必须删除 Namespace="" 属性以使请求部分正常工作”的地方,不要这样做,而是使用唯一的字符串。
猜你喜欢
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-12
  • 2018-08-30
相关资源
最近更新 更多