Managing / Modifying Deserialisation Faults / Intercepting Responses

1.1k views Asked by At

Let's say I have the following request object:

[DataContract]
public class MyContract {
    [DataMember]
    public Guid Token { get; set; }
}

And a WCF service definition as follows:

[ServiceContract]
public interface IMyService {
    [OperationContract]
    bool Validate(MyContract request);
}

If I send the following to the operation, I get the desired response:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:esi="http://mynamespace.com/" xmlns:con="http://mynamespace.com">
   <soapenv:Header>
   </soapenv:Header>
   <soapenv:Body>
      <esi:Validate>
         <esi:Token>9192ef6a-819f-4a8a-8fde-4125999e33dc</esi:Token>
      </esi:Validate>
   </soapenv:Body>
</soapenv:Envelope>

If I send an invalid Guid (this occurs with any type), I get the following response:

<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">a:InternalServiceFault</faultcode>
         <faultstring xml:lang="en-GB">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 &lt;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>

Which is all well and good, but not really enough information for my consumers, considering my service knows exactly what's wrong with the data.

I can expose the full exception with the <serviceDebug includeExceptionDetailInFaults="true"/> web config setting, but that's TOO MUCH information for my consumers! I would rather customise the error at the code level, but I'm not sure how I can attach to the deserializer? I know how to deal with custom SOAP faults and FaultContracts, but this seems to be at a lower level - I need to intercept the incoming message before it hits the CLR methods, somehow? Is there a way to do this that I'm not aware of?

2

There are 2 answers

6
EdmundYeung99 On BEST ANSWER

The deserializer is on the IDispatchMessageFormatter

public class MyFormatter : IDispatchMessageFormatter
{
    readonly IDispatchMessageFormatter _originalFormatter;

    public MyFormatter(IDispatchMessageFormatter originalFormatter)
    {
      _originalFormatter = originalFormatter;
    }

    public void DeserializeRequest(Message message, object[] parameters)
    {
        try
        {
            _originalFormatter.DeserializeRequest(message, parameters);
        }
        catch(Exception ex)
        {
            //throw custom fault here
        }
    }

    public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
    {
        return _originalFormatter.SerializeReply(messageVersion, parameters, result);
    }

You can hook this up via an OperationBehavior:

public class MyOperationBehavior : IOperationBehavior
{
    public void Validate(OperationDescription operationDescription) { }
    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Formatter = new MyFormatter(dispatchOperation.Formatter);
    }
    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }

}

Hook up the OperationBehavior to your service operations via an attribute.

If you need to use configuration, attach them via a ServiceBehavior or EndpointBehavior


You can catch errors and handle them by implementing the IErrorHandler.

Try attaching this to a service behavior:

public class ServiceExceptionBehaviour : BehaviorExtensionElement, IServiceBehavior, IErrorHandler
{
   //...
   //implement all required methods
   //...
}
0
Eugene Osovetsky On

I assume that you want to convert the exception that's thrown by the deserializer (when deserializing an invalid Guid) to a proper SOAP Fault with the right level of detail. There are two extensibility points I know of that may help you here - an IErrorHandler ( http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler.aspx ) and a FaultConverter ( http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.faultconverter.aspx ). Can't tell you off the top of my head whether they will actually be able to do what you want, but I hope it will be a good starting point. The idea there is that you will get a chance to examine all exceptions that arise and convert them to faults yourself, rather than relying on the default fault.