WCF Crib Sheet – Error Handling

SOAP Fault

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <s:Fault>
            <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher" xmlns="">a:InternalServiceFault</faultcode>
            <faultstring xml:lang="en-GB" xmlns="">The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug>configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring>
        </s:Fault>
    </s:Body>
</s:Envelope>
  • When a .NET exception is thrown during the processing of a request, the server responds with a SOAP Fault message.
  • By default, for security reasons, the SOAP Fault message is generic and does not contain the exception details.
  • The SOAP Fault contains faultcode and a faultstring elements.

IncludeExceptionDetailsInFault

<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <s:Fault>
            <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher" xmlns="">a:InternalServiceFault</faultcode>
            <faultstring xml:lang="en-GB" xmlns="">Sequence contains no elements</faultstring>
            <detail xmlns="">
                <ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                    <HelpLink i:nil="true"></HelpLink>
                    <InnerException i:nil="true"></InnerException>
                    <Message>Sequence contains no elements</Message>
                    <StackTrace>
                        ....
                    </StackTrace>
                    <Type>System.InvalidOperationException</Type>
                </ExceptionDetail>
            </detail>
        </s:Fault>
    </s:Body>
</s:Envelope>
  • To debug a service, we can configure the service to include the exception details in the SOAP fault message.
  • Set the includeExceptionDetailInFaults config setting to true.
  • In addition to the faultcode and faultstring elements, the SOAP Fault now contains a detail element.

Explicity handling exceptions with FaultException

public ProductDto Get(int productId)
{
    try
    {
        Product product = _service.GetProduct(productId);
        return Mapper.Map<Product, ProductDto>(product);
    }
    catch (Exception ex)
    {
        throw new FaultException(
            new FaultReason(ex.Message),
            new FaultCode("Exception"));
    }
}
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <s:Fault>
            <faultcode xmlns="">s:Exception</faultcode>
            <faultstring xml:lang="en-GB" xmlns="">Sequence contains no elements</faultstring>
        </s:Fault>
    </s:Body>
</s:Envelope>
  • Error details can be returned to the client as an un-typed Fault, by throwing a new instance of System.ServiceModel.FaultException.
ProductServiceClient client = new ProductServiceClient();
ProductDto product = null;

try
{
    product = client.GetById(11);
    Console.WriteLine(product.Name);
}
catch (FaultException fex)
{
    Console.WriteLine(fex.Message);
}
finally 
{
    Console.ReadLine();
}
  • A FaultException can be caught and interrogated by the client.

Strongly Typed Faults

[DataContract]
public class ServiceFault
{
    public ServiceFault(string message)
    {
        Message = message;
    }

    [DataMember]
    public string Message { get; set; }
}

[DataContract]
public class ValidationFault : ServiceFault
{
    public ValidationFault(string message)
        : base(message)
    {
        ValidationErrors = new List<ValidationError>();
    }

    public ValidationFault(string message, List<ValidationError> validationErrors)
        : base(message)
    {
        ValidationErrors = validationErrors;
    }

    [DataMember]
    public List<ValidationError> ValidationErrors { get; set; }
}

[DataContract]
public class ValidationError
{
    public ValidationError(string property, List<string> errorMessages)
    {
        Property = property;
        ErrorMessages = errorMessages;
    }

    [DataMember]
    public string Property { get; set; }
    [DataMember]
    public List<string> ErrorMessages { get; set; }
}
  • Define one or more classes to contain the fault details.
  • The classes and properties should be decorated with the [DataContract] and [DataMember] attributes so that they are known to the client.
  • The classes can be based on System.Exception, although at the risk of revealing implementation details with the StackTrace property.
[ServiceContract(Namespace = "BikeStore.WcfServiceLibrary")]
public interface IProductService
{
    [OperationContract(Name = "GetById")]
    [FaultContract(typeof(ValidationFault))]
    ProductDto Get(int productId);
}
  • Decorate the service operation with one or more [FaultContract] attribute.
  • A [FaultContract] attribute is required for the operation to return a strongly type fault. If the attribute is omitted the service will instead return an un-typed fault.
public ProductDto Get(int productId)
{
    Product product = _service.GetProduct(productId);
    if (product == null)
    {
        throw new FaultException<ValidationFault>(new ValidationFault("Product is not valid"));
    }
    return Mapper.Map<Product, ProductDto>(product);
}
  • When a strongly typed fault exception is thrown the server will return a strongly typed fault.
  • For any other exception, and un-typed fault will be returned.
ProductServiceClient client = new ProductServiceClient();
ProductDto product = null;

try
{
    product = client.GetById(11);
    Console.WriteLine(product.Name);
}
catch (FaultException<ValidationFault> vf)
{
    Console.WriteLine(vf.Detail.Message);
}
catch (FaultException fex)
{
    Console.WriteLine(fex.Message);
}
finally
{
    Console.ReadLine();
}
  • Strongly typed faults enable the client to easily differentiate between classes of faults.
  • Strongly typed faults also enable additional error information to be sent to the client.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s