Search code examples
.netwcfxml-serializationnamespaceswcf-client

Multiple name spaces in a soap fault message causing FaultException deserialization to fail


We're connecting to a web service and the fault message we're getting back isn't deserializing (at all), and no version of class that I can make will deserialize correctly. We have no control over the server side of things. The server does not allow for discovery, so adding ?WSDL to the end of the URL of the endpoint results in an error, not a WSDL.

[Fiddler][1] shows the Fault message coming back looks like:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:eGov="http://eGov.gov" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <soapenv:Fault>
    <faultcode>Client</faultcode>
    <faultstring/>
      <detail>
        <eGov:eGov2Exception>
          <eGov:ErrorClassification>SOME_ERROR</eGov:ErrorClassification>
          <eGov:ErrorCode>SOME_ERROR_CODE</eGov:ErrorCode>
          <eGov:ErrorMessage>Your request was unsuccessful. blah blah blah.</eGov:ErrorMessage>
        </eGov:eGov2Exception>
      </detail>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

Yet no class we've made (trying xsd.exe, svcutil and others including code we've written from scratch) can deserialize it when we try to catch it with:

catch (FaultException<eGov2ExceptionType> exp)
  {
     // Never stops here. 
  }
catch (FaultException<AllOtherAttemptedClasses> exp)
  {
     // Never stops here. 
  }
catch (SoapException se)
  {
     // Never stops here. 
  }
catch (FaultException exp)
  {
     //Always gets caught here. 
  }

Only the base FaultException catch will get called, meaning we lose the contents of the FaultMessage being sent. Some of the classes I've written will serialize really closely to the sample above, but fail to deserialize it, so our suspicion is that there is a namespace issue.

Questions:

1 - How would you write this?

2 - Is this a common error/issue with WCF?

[1]: http://www.fiddler2.com/fiddler2/ Fiddler


Solution

  • What we ended up doing was to give up trying to catch the Fault and pass it back in the SOAP channel. Instead, we created a custom exception, and wired up a MessageInspector to watch for the faults and throw it as an exception.

    The relevant part of the (sanitized) code:

       public void AfterReceiveReply(ref Message reply, object correlationState)
        {
            if (reply.IsFault)
            {
                XmlDictionaryReader xdr = reply.GetReaderAtBodyContents();
                XNode xn = XDocument.ReadFrom(xdr);
                string s = xn.ToString();
                XDocument xd = XDocument.Parse(s);
                XNamespace nsSoap = "http://schemas.xmlsoap.org/soap/envelope/";
                XNamespace ns = "http://eGov.gov";
                XElement xErrorClass = xd.Element(nsSoap + "Fault").Element("detail").Element(ns + "eGov2Exception").Element(ns + "RequestErrorClassification");
                XElement xErrorCode = xd.Element(nsSoap + "Fault").Element("detail").Element(ns + "eGov2Exception").Element(ns + "RequestErrorCode");
                XElement xErrorMessage = xd.Element(nsSoap + "Fault").Element("detail").Element(ns + "eGov2Exception").Element(ns + "RequestErrorMessage");
    
                throw new eGovException(xErrorClass.Value, xErrorCode.Value, xErrorMessage.Value);
            }
        }
    

    The main application then uses:

    catch (eGovException ex)
    {
    // Handles exception here.
    }

    Much too much time was wasted trying to correct name spaces. Thanks for answering.