WCF with UPS RateWS Service FaultException.Detail<T> always an empty array

950 views Asked by At

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

There are 3 answers

0
Brian Reiter On BEST ANSWER

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.

2
Chris G.Royle On

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.

0
rattymml On

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[].