How to Handle WCF Fault Exception
Asked Answered
C

3

9

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() {

    }
}
Contemplation answered 13/6, 2013 at 21:56 Comment(0)
M
3

I'm using the math example from the article you referred to. Create the fault class:

[DataContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public class MathFault
{    
...
}

Decorate your method with OperationContract, e.g.

[OperationContract]
[FaultContract(typeof(MathFault))]
int Divide(int n1, int n2);

Create your MathFault object with sensible data. Wrap it and throw it:

throw new FaultException<MathFault>(yourFault);

Hope this helps.

Monzonite answered 13/6, 2013 at 22:46 Comment(1)
thanks! I think i'm on the right track, but when defining my Fault class, what does that class need to look like to properly handle the fault in the example I sent above? Also, how in the client code do I determine what Fault has been thrown, I'm assuming that's taken care of in the System.ServiceModel.FaultException.CreateFault code?Contemplation
M
3

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
}
Maite answered 14/6, 2013 at 1:15 Comment(2)
Thanks @Jason, but my Fault is never caught as a InvalidRepresentation. It's always caught as a generic FaultException. I have the DataContract defined in my InvalidRepresentation class. I don't understand how it knows what type of Fault is being thrown??Contemplation
On the service side, are you throwing the fault in the manner that Fredrik described below?throw new FaultException<InvalidRepresentation>(yourFault);Maite
C
3

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)

Contemplation answered 14/6, 2013 at 17:35 Comment(2)
good pattern mapper FaultException<RepresentationFailures> to Exception ?Ric
Don't throw Exception. Create your own custom exception and pass what you need into that.Chipboard

© 2022 - 2024 — McMap. All rights reserved.