Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Handle WCF Fault Exception

Tags:

c#

soap

wcf

fault

I am trying to add more information regarding a SOAP fault in a open source client application. The client is setup to call "HandleFault" whenever it encounters any SOAP fault. The Handle Fault method is shown below:

   public static void HandleFault(Message message) {
        MessageFault fault = MessageFault.CreateFault(message, Int32.MaxValue);
        throw System.ServiceModel.FaultException.CreateFault(fault,
            typeof(PermissionDeniedFault),
            typeof(EndpointUnavailable),
            typeof(InvalidRepresentation),
            typeof(UnwillingToPerformFault),
            typeof(CannotProcessFilter),
            typeof(AnonymousInteractionRequiredFault)
        );
    }

Here is a portion of the SOAP Fault that is passed in as "message" when I try and do something like change a phone number to invalid format from the client.

  <s:Body u:Id="_2">
<Fault xmlns="http://www.w3.org/2003/05/soap-envelope">
  <Code>
    <Value>Sender</Value>
    <Subcode>
      <Value xmlns:a="http://schemas.xmlsoap.org/ws/2004/09/transfer">a:InvalidRepresentation</Value>
    </Subcode>
  </Code>
  <Reason>
    <Text xml:lang="en-US">The request message contains errors that prevent processing the request.</Text>
  </Reason>
  <Detail>
    <RepresentationFailures xmlns="http://schemas.microsoft.com/2006/11/ResourceManagement" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <AttributeRepresentationFailure>
        <AttributeType>OfficePhone</AttributeType>
        <AttributeValue>(123)456-7890</AttributeValue>
        <AttributeFailureCode>ValueViolatesRegularExpression</AttributeFailureCode>
        <AdditionalTextDetails>The specified attribute value does not satisfy the regular expression.</AdditionalTextDetails>
      </AttributeRepresentationFailure>
      <CorrelationId>11042dda-3ce9-4563-b59e-d1c1355819a4</CorrelationId>
    </RepresentationFailures>
  </Detail>
</Fault>

Whenever that Fault is encountered, the client only returns back "The request message contains errors that prevent processing the request.", I would like to include the "AttributeRepresentationFailure" node and child nodes before re-throwing the exception in the client.

The way I understand it is that I need to define a Fault class that contains those details to be deseralized, so that the call to "CreateFault" can return a . I've read through http://msdn.microsoft.com/en-us/library/ms733841.aspx but I just don't understand exactly how to define the class so that the client knows what type of fault is thrown.

UPDATE

In the client side handle fault method I added

 try
        {
            throw faultexcept;
        }
        catch (System.ServiceModel.FaultException<InvalidRepresentation> invalidRepresentationFault)
        {
            throw invalidRepresentationFault;
        }
        catch (System.ServiceModel.FaultException otherFault)
        {
            throw otherFault;
        }
        catch (Exception ex)
        {
            throw ex;
        }

The fault is always caught under the base fault class "otherFault". My InvalidRepresentation class is defined as below

 [DataContract(Namespace = Constants.Rm.Namespace)]
public class InvalidRepresentation 
{
    private string _attributeType;
    private string _attributeValue;
    private string _attributeFailureCode;
    private string _additionalTextDetails;

    [DataMember]
    public string AttributeType
    {
        get { return _attributeType; }
        set { _attributeType = value; }
    }

    [DataMember]
    public string AttributeValue
    {
        get { return _attributeValue; }
        set { _attributeValue = value; }
    }

    [DataMember]
    public string AttributeFailureCode
    {
        get { return _attributeFailureCode; }
        set { _attributeFailureCode = value; }
    }

    [DataMember]
    public string AdditionalTextDetails
    {
        get { return _additionalTextDetails; }
        set { _additionalTextDetails = value; }
    }


    public InvalidRepresentation() {

    }
}
like image 406
The Unique Paul Smith Avatar asked Jun 13 '13 21:06

The Unique Paul Smith


2 Answers

To add to Fredrik's answer, your Fault class can be whatever you need to convey the details of your custom error to the client. It doesn't have to inherit from another class or implement an interface. It just needs to be marked with the DataContract attribute.

As for catching it on the client side:

try
{
    ...
}
catch (FaultException<MathFault> mathFault)
{
    // handle a math fault
}
catch (FaultException<OtherCustomFault> otherFault)
{
    // handle another type of custom fault
}
catch (Exception ex)
{
    // regular exception handling
}
like image 60
Jason Avatar answered Oct 18 '22 11:10

Jason


I was finally able to find a resolution to this, thanks to everyone who helped! This isn't the best solution and needs to be cleaned up, but it works while until I learn more about WCF and SOAP Faults. Also, I don't have access to the service code, just the client code. The client code is being ran as a powershell module.

InvalidRepresentationFault Class

using System.Runtime.Serialization;
namespace Client.Faults {
    public class InvalidRepresentationFault 
    {
        public InvalidRepresentationFault() {}
    }
    [DataContract(Namespace = Constants.Rm.Namespace)]
    public class RepresentationFailures
    {
        [DataMember()]
        public FailureDetail AttributeRepresentationFailure;

    [DataContract(Namespace = Constants.Rm.Namespace)]
    public class FailureDetail
    {
        [DataMember(Order = 1)]
        public string AttributeType;

        [DataMember(Order = 2)]
        public string AttributeValue;

        [DataMember(Order = 3)]
        public string AttributeFailureCode;

        [DataMember(Order = 4)]
        public string AdditionalTextDetails;
    }

    [DataMember]
    public string CorrelationId;
}

}

Client HandleFault Code

 public static void HandleFault(Message message) {
        MessageFault fault = MessageFault.CreateFault(message, Int32.MaxValue);

        //Let the fault exception choose the best fault to handle?
        System.ServiceModel.FaultException faultexcept = System.ServiceModel.FaultException.CreateFault(fault,
            typeof(PermissionDeniedFault),
            typeof(AuthenticationRequiredFault),
            typeof(AuthorizationRequiredFault),
            typeof(EndpointUnavailable),
            typeof(FragmentDialectNotSupported),
            typeof(InvalidRepresentationFault),
            typeof(UnwillingToPerformFault),
            typeof(CannotProcessFilter),
            typeof(FilterDialectRequestedUnavailable),
            typeof(UnsupportedExpiration),
            typeof(AnonymousInteractionRequiredFault),
            typeof(RepresentationFailures)
        );

        try
        {
            throw faultexcept;
        }
        catch (System.ServiceModel.FaultException<RepresentationFailures> invalidRepresentationFault)
        {
            throw new Exception(
                String.Format(
                    "{0}\r\nfor Attribute \"{1}\" with Value \"{2}\"",
                        invalidRepresentationFault.Detail.AttributeRepresentationFailure.AdditionalTextDetails,
                        invalidRepresentationFault.Detail.AttributeRepresentationFailure.AttributeType,
                        invalidRepresentationFault.Detail.AttributeRepresentationFailure.AttributeValue
                    ),
                 invalidRepresentationFault
             );
        }
        catch (System.ServiceModel.FaultException otherFault)
        {
            throw otherFault;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

Now when the service throws a SOAP Fault it gets deserialized into the "RepresentationFailures" class, which I can customize before re-throwing back upstream (in this case to powershell)

like image 40
The Unique Paul Smith Avatar answered Oct 18 '22 11:10

The Unique Paul Smith