views:

372

answers:

3

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/"&gt;
  <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

+1  A: 

One way to handle this if serialization is failing completely is to use a OoperationContract using the Message type as input and output. This way you can manually parse the XML when Iffault == true or use GetBody() to get the regular contents if no error occurred.

Maurice
+1  A: 

First of all, if you cannot get the WSDL from the people who produced this web service, then they have no business having a SOAP-based web service. A proper WSDL would solve your problem, whether or not "?WSDL" is used.

Second, please post the code of the eGov2ExceptionType class. I suspect it does not have the http://eGov.gov namespace set on it.

John Saunders
WSDLs were mailed to "qualified partners" of which we are one. On Monday, I'll post the code of the most close class I've made.
Tangurena
If you've got a WSDL, then you should be able to supply it to svcutil.exe and get what you need. Try that and see if svcutil produces any error or warning messages. If none are displayed, then look in the generated classes - sometimes warnings appear as comments in the generated code.
John Saunders
+1  A: 

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.

Tangurena