I've created a WCF service that uses Unity/Unity.WCF/Unity.Interceptors for fault handling.
Now, If I do a SOAP request and do NOT include a required node in the request - the service method executes - I throw an exception and my interceptor makes this into a SOAP fault.
Example:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v10="http://services.....nl/.../Schemas/v10">
<soapenv:Header/>
<soapenv:Body>
<v10:TheRequestObject>
</v10:TheRequestObject>
</soapenv:Body>
</soapenv:Envelope>
I can step through the service call using the debugger - I throw the exception when validating the request object and my interceptor makes this into a SOAP fault:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="nl-NL">Error msg</faultstring>
<detail> ...
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
Now - our tester provided an empty node for the required parameter like so:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v10="http://services.....nl/.../Schemas/v10">
<soapenv:Header/>
<soapenv:Body>
<v10:WagenlijstRequest>
<v10:RequiredInteger></v10:RequiredInteger>
</v10:WagenlijstRequest>
</soapenv:Body>
</soapenv:Envelope>
And the service returns the following error message:
<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="nl-NL">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>
The thing is - this request never reaches my service, so I cannot do anything with this error message. How can I influence what happens here?
This reminds me a bit of modelbinding in MVC - can I influence the binding behavior or so?
OK, I got it. Here's what you need to do:
Implment an IOperationBehavior with something like
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { dispatchOperation.Formatter = new ExceptionHandlingFormatter(); }
Create the formatter that implements IDispatchMessageFormatter and do something like this:
public void DeserializeRequest(Message message, object[] parameters)
{
var serializer = new XmlSerializer(typeof(WagenlijstRequest), "http://services.prorail.nl/OVGS/Schemas/v10");
try
{
// Attempts to deserialize the object given the message body
var result = serializer.Deserialize(message.GetReaderAtBodyContents());
parameters[0] = new SomeRequest(result as AnotherType);
}
catch (Exception e)
{
// In case of an exception - wraps the exception in a Dutch one + logs it
var exception = new RequestDeserializationException(e);
this.log.Fatal(string.Format("Error while deserializing the following request:\r\n{0}\r\nException:\r\n{1}", message, exception), exception);
throw new SomeFaultType(exception).AsFaultException();
}
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
return Message.CreateMessage(messageVersion, string.Empty, result);
}
It comes down to being able to apply my custom desialization behevior for the operation. That behavior deserializes but with a try catch that wraps the exception in an exception with a Dutch message.