wcf icon indicating copy to clipboard operation
wcf copied to clipboard

dotnet-svcutil silently failing to deserialize responses

Open brent-williams opened this issue 5 years ago • 15 comments

Describe the bug The proxy from dotnet-svcutil connects to the server, serializes and sends a request ok, and elicits a correct response from WFC server. But deserialization fails without error and I simply get null returned. Under .NET Framework 4.8 the wsdl.exe-generated proxy there is deserializing fine.

To Reproduce The SOAP server is fixed and beyond my control. However, I have captured the relevant SOAP request & response in Fiddler, attached with the relevant WSDL. Connect/operation code below, nothing special. Note in main() I am using @shmao's workaround from #2219 to avoid the 'JScript/CSharp scripts is not supported' proxy exception i.e.:

            MethodInfo method = typeof(XmlSerializer).GetMethod("set_Mode", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
            method.Invoke(null, new object[] { 1 });

Proxy generated with dotnet-svcutil --sync --outputDir . http://XXX/?WSDL. The generated proxy fails to generate unless all Namespace="" attribute arguments are removed, possibly related. I added EventListener and dumping everything down Verbose from all event sources, lots of output but nothing at all around the deserialization failure.

Putting dotnet-svcutil aside I wrote manual Channel-based MessageContract/DataContract wrappers, ultimately I get the same null result. I am unable to shape the Response classes so that attached SOAP response will deserialize under WCF with a non-null result.

Confirmed the null result occurs using dotnet-svcutil 2.0.1 with System.ServiceModel.* 4.7.0 under both .NET Core 3.0 and 3.1 on both Windows 10 and macOS Catalina.

Expected behavior A populated response object. Failing that, an exception to help narrow down the problem. Failing that, even just some hack to get at the raw response string in case of failure, that way I could at least use the proxy for requests & tear apart the response manually (regex/xpath/etc.).

Screenshots Attached fiddler.

Additional context Connection code

      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;
      }

Relevant WSDL, can send in full on private channel if required.

<?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>

Request Response

brent-williams avatar Feb 14 '20 02:02 brent-williams

Added to stackflow as well (https://stackoverflow.com/questions/60332365/net-core-3-1-dotnet-svcutil-generated-wcf-proxy-and-even-a-custom-channel-a), critical blocker for us.

brent-williams avatar Feb 21 '20 04:02 brent-williams

@brent-williams We will need the full wsdl so we can reproduce the issue, also for a visual examination the part of the wsdl with all the contract info is very useful. If you need to hide the endpoint address it should be fine for you to edit that out.

StephenBonikowsky avatar Feb 24 '20 22:02 StephenBonikowsky

Hi @StephenBonikowsky thanks for reply. Unfortunately I can't send full WSDL without private channel + NDA, however I have stripped it down to the method in question. dotnet-svcutil runs produces similar output against the below as it does the full WSDL. Is this sufficient for you to attempt a dup?

<?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>

brent-williams avatar Feb 25 '20 08:02 brent-williams

@brent-williams We'll have someone work on a repro and see if there is anything else we need. @imcarolwang Could you have someone work on reproducing this issue?

StephenBonikowsky avatar Feb 25 '20 23:02 StephenBonikowsky

@mconnew Is there any other info we could use to help @imcarolwang reproduce the issue.

StephenBonikowsky avatar Feb 26 '20 18:02 StephenBonikowsky

It looks like there's enough of the WSDL there to reproduce the generated client code. Just to note, this isn't a WCF service. He's also provided screenshots of a request and response so we know what to mock out the server response to be. I think we have everything needed.

mconnew avatar Feb 26 '20 19:02 mconnew

Great anything I can do to help let me know. True the server is not WCF, was written in C/gSOAP.

brent-williams avatar Feb 27 '20 02:02 brent-williams

FYI this problem was duped & a workaround found for the stripped-down-wsdl only on stackoverflow (https://stackoverflow.com/questions/60332365/dotnet-svcutil-generated-wcf-proxy-and-even-a-custom-channel-are-returning-n). A solution to generate a usable proxy directly from dotnet-svcutil for the full wsdl remains outstanding.

brent-williams avatar Mar 02 '20 03:03 brent-williams

We have a repro now. Will need to get some cycles freed-up to investigate this further.

@brent-williams You said this is a blocker for you, does the workaround found on stackoverflow unblock you?

StephenBonikowsky avatar Mar 03 '20 18:03 StephenBonikowsky

@StephenBonikowsky great you have a repro. The stackoverflow fix works for single-method WSDL, but for 2+ methods I couldn't find a combination of namespace values to get the proxy built that both ran and produced a non-null response object for both methods. But by splitting the WSDL into multiples files, generating a proxy for each and applying the fix to each proxy I can get things running for multiple methods, at the expense of the manual file work and calling OpenAsync() on the proxies before each method. So no longer blocked but workaround is ugly.

brent-williams avatar Mar 04 '20 05:03 brent-williams

Ok, thank you for the additional info. We would like to get this fixed and will see if it can be incorporated into the next release.

StephenBonikowsky avatar Mar 04 '20 16:03 StephenBonikowsky

Here is another wsdl that fails, although you need an account to test it. https://iwstraining.iridium.com:8443/iws-current/iws?wsdl

egorpavlikhin avatar Jul 27 '20 11:07 egorpavlikhin

@mconnew can you please take a look when you get a chance?

HongGit avatar Oct 29 '20 22:10 HongGit

@imcarolwang can you please see if you could put together a repro?

HongGit avatar Jun 23 '21 23:06 HongGit

Is there and root cause identified for this problem yet? Reason I'm asking is because the SO workaround did not work for me.

Also, from the issue it is not clear if it is a bug in WCF that it is not being able to deserialise the response properly, or with dotnetsvc-util for not generating the proxy class correctly? or both?

akhilesh84 avatar Apr 17 '22 08:04 akhilesh84