0

I'm having a problem getting the soap 1.1 fault detail element using a .NET 4.5 WCF client for UPS RateWS service.

The issue is that while the faultcode and faultstring elements are coming back fine as .Code and .Message properties on the exception. The detail object is not being deserialized correctly and is always an empty array.

I'm generating the WCF client by unpacking the wsdl and xsds from the UPS Rating developer kit Rates_Pkg_Gnd.zip file SCHEMA-WSDLs directory and pointing Visual Studio 2013s Add Service Reference dialog at the RateWS.wsdl on my file system.

An actual wire soap fault message looks like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header />
  <soapenv:Body>
    <soapenv:Fault>
      <faultcode>Client</faultcode>
      <faultstring>An exception has been raised as a result of client data.</faultstring>
      <detail>
        <err:Errors xmlns:err="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1">
          <err:ErrorDetail>
            <err:Severity>Hard</err:Severity>
            <err:PrimaryErrorCode>
              <err:Code>111285</err:Code>
              <err:Description>The postal code 21740 is invalid for AB Canada.</err:Description>
            </err:PrimaryErrorCode>
          </err:ErrorDetail>
        </err:Errors>
      </detail>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

I have tried catching System.ServiceModel.FaultException<UPS.RateService.ErrorDetailType[]> but the Detail property is always an array of UPS.RateService.ErrorDetailType[0] -- zero size.

Similarly catching a FaultException and calling .CreateMessageFault() to access .GetDetail<XmlElement>() yields an XML object containing an ArrayOfErrorDetailType element with nothing in it. The alternate approach of using .GetReaderAtDetailContents() to get an XmlReader yields the same bogus structure.

This is the error message xsd:

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:error="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" elementFormDefault="qualified" targetNamespace="http://www.ups.com/XMLSchema/XOLTWS/Error/v1.1" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="Errors">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element maxOccurs="unbounded" name="ErrorDetail" type="error:ErrorDetailType" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:complexType name="ErrorDetailType">
    <xsd:sequence>
      <xsd:element name="Severity" type="xsd:string" />
      <xsd:element name="PrimaryErrorCode" type="error:CodeType" />
      <xsd:element minOccurs="0" name="MinimumRetrySeconds" type="xsd:string" />
      <xsd:element minOccurs="0" name="Location" type="error:LocationType" />
      <xsd:element minOccurs="0" maxOccurs="unbounded" name="SubErrorCode" type="error:CodeType" />
      <xsd:element minOccurs="0" maxOccurs="unbounded" name="AdditionalInformation" type="error:AdditionalInfoType" />
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="CodeType">
    <xsd:sequence>
      <xsd:element name="Code" type="xsd:string" />
      <xsd:element name="Description" type="xsd:string" />
      <xsd:element minOccurs="0" name="Digest" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="AdditionalInfoType">
    <xsd:sequence>
      <xsd:element name="Type" type="xsd:string" />
      <xsd:element maxOccurs="unbounded" name="Value" type="error:AdditionalCodeDescType" />
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="AdditionalCodeDescType">
    <xsd:sequence>
      <xsd:element name="Code" type="xsd:string" />
      <xsd:element minOccurs="0" name="Description" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="LocationType">
    <xsd:sequence>
      <xsd:element minOccurs="0" name="LocationElementName" type="xsd:string" />
      <xsd:element minOccurs="0" name="XPathOfElement" type="xsd:string" />
      <xsd:element minOccurs="0" name="OriginalValue" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>
Brian Reiter
  • 1,339
  • 1
  • 10
  • 16

3 Answers3

4

I realise this is an old Q, but I found the solution on another question - FaultException.Detail coming back empty

Renaming the generated class from ErrorDetailType to ErrorDetail meant I could iterate the errors.

Chris G.Royle
  • 141
  • 1
  • 6
  • Prior to the edit this answer wasn't an answer. At the most it was link only. For reference please see [answer]. That said if you feel this is a valuable solution to the question and that the question is in fact not a duplicate then I'll retract my flag and previous comment. Thank you. – Bugs Apr 10 '18 at 10:49
  • 1
    Thank you so much! This is the best answer here by far. All I did is rename the class to ErrorDetail, and now when there is a FaultException ex, I can do ex.Detail[0].PrimaryErrorCode.Description to get the description! I spent most of the day because the UPS API was failing with the cryptic "An exception has been raised as a result of client data" Reason. Now that I can see the actual error description, I can see that the weight UnitOfMeasurement being passed in was KG, instead of KGS. I scoured the input for hours, and couldn't find this cause without your fix. – Tolga Apr 29 '19 at 22:39
3

To get this to work I added a dummy element inside the sequence:

<xsd:element name="Errors">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element name="ErrorDetail" type="error:ErrorDetailType" maxOccurs="unbounded"/>
            <xsd:element name="TestElement" type="xsd:string" minOccurs="0"/>
        </xsd:sequence>

    </xsd:complexType>
</xsd:element>

For some reason that allows WCF to serialize the fault correctly. This forces WCF to generate a new class "Errors" that is passed in the fault contract. You could then catch the FaultException<Errors> exception and have access to the ErrorDetailType[].

rattymml
  • 31
  • 4
0

Ultimately the solution I found was modify the WSDL so that the contents of the <detail /> element are not defined and use svcutil to generate the proxy classes. When I do this, I get access to the raw <err:Errors /> XML element:

var fault = ex.CreateMessageFault();
var faultXml = fault.GetDetail<XmlElement>();

This allows me to use XPath to get at the contents of the err:Description element. With the original WSDL, I get nothing.

Brian Reiter
  • 1,339
  • 1
  • 10
  • 16