1

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?

Steve Konves
  • 2,648
  • 3
  • 25
  • 44
Spikeh
  • 3,540
  • 4
  • 24
  • 49

2 Answers2

2

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
   //...
}
EdmundYeung99
  • 2,461
  • 4
  • 27
  • 43
  • That would explain it - I was looking at IClientMessageInspector, IDispatchMessageInspector and failing to find anything. I'll give this a try asap :) – Spikeh Oct 24 '12 at 07:22
  • Struggling to get this actually capturing anything - implemented the BehaviorExtentionElement in the web config, and added it to my default serviceBehavior, but none of the methods in MyFormatter are being called :| – Spikeh Oct 26 '12 at 14:09
  • The ServiceBehavior was for attaching the IErrorHandler if you wanted, but the Formatter is attached to the OperationBehavior. How have you attached the OperationBehavior to your service? you can do so by adding an Attribute on your service methods, or attaching it to an endpoint/service behavior – EdmundYeung99 Oct 28 '12 at 20:27
  • I've been trying to do everything via WCF configuration (adding a behaviorExtension and adding it to the service behaviors list. It's something I'd like to make generic, turn on and off, and apply to new services as I write them. I actually managed to get some breakpoints trapping, but I had to move IServiceBehavior and IErrorHandler to the class that implements IOperationBehavior. BehaviorExtensionElement is now a class on its own. – Spikeh Oct 29 '12 at 08:06
  • so did you manage to get it working? let me know if you need more assistance. Maybe add an updated section of the code you have so far. – EdmundYeung99 Oct 29 '12 at 21:00
  • Still working on it (something else has come up to do with IOperationInvoker that's taking priority atm), though you've pointed me in the right direction :) I'll post code when I get something finalized! – Spikeh Oct 30 '12 at 11:38
0

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.

Eugene Osovetsky
  • 6,443
  • 2
  • 38
  • 59