简体   繁体   中英

How to Handle WCF Fault Exception

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

    }
}

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
}

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)

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, eg

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM