WCF, IErrorHandler and propagating faults
On a recent project, I have needed to set up a generic error handler just above the service layer which ensured that only known exceptions were propagated up to the client in a controlled manner. This post contains information about how to write an IErrorHandler, but more importantly, it does contain an important point at the end about its implementation that I wanted to share, as well as save for my own reference on this blog, in this wonderful inter-web thingy.
Update: The code for this article is here
Using FaultContracts is a good way of achieving this WCF, but I also wanted to be able to:
- Catch generic CLR exceptions and propagate them to the client as a generic fault exceptions.
- Catch some NHibernate specific exceptions (the concurrency ones in particular) and propagate these to the client as a general concurrency fault.
This was all the client needed to know, and also provided an opportunity to log any unhandled exceptions that may sneak there way up to the services. WCF provides great exception shielding by default, but we wanted a bit more control.
Enter the IErrorHandler interface that allows you to intercept any faults or exceptions before they are sent back to the client. To use this properly, you still need to define your FaultContract as required. My simple example contract is as follows:
So to start the implementation of my custom error handler, I define a class that will implement an IErrorHandler as well as IServiceBehavior.
I do this because not only do I want to create an IErrorHandler implementation, but I need it to participate as a behavior within the WCF pipeline, that is, I want to add it in as a ServiceBehavior when I create the host so that my custom error handler implementation can be plugged in.
I first provide implementations for the IErrorHandler.
HandleError returning true simply means I have handled this error and don't need other error handlers to deal with it.
ProvideFault does a few things. First it checks if its a genuine FaultException. If so, it does nothing but let it pass through. If it isn't, then a FaultException<ApplicationFault> is created, and a message fault is generated from that. Then a new message is created, using the message fault is its content, and that message is assigned to the return fault parameter.
If I wanted to catch another type of exception, I might check for it something like:
if (error is NHibernate.StaleObjectException) .....
and optionally create a different type of fault. The ApplicationFault used to construct the FaultException is just a standard DataContract.
Implementing the IServiceBehavior is pretty easy as you see below:
If you wanted to, in the Validate method, you could iterate through each operation and ensure its got your required FaultContract applied like this:
As a nice touch, I wanted this error handler to be able to added via configuration and play nicely with the WCF Service Configuration editor. To do this, you need a class that inherits from BehaviorExtensionElement and implements 2 simple methods like this:
So, finally we are at the point that caught me up for a while. In the implementation of the ProvideFault method of my error handler, you need to specify an action for the returned Fault as shown below:
Now its important that this Action is a known action identifier that your client is expecting. In other words, if you generate a proxy (via SvcUtil) for this service and peek into the reference.cs file for the 'Action' attribute on your services fault contract, make sure they are the same. Otherwise, when your error handler generates a fault with an unknown fault action, the client wont accept it as a known fault and it will look like just a generic FaultException, not the specific FaultContract<YourType> you were after.
When I was developing this error handler, I simply fudged this value while I got on with the task of implementing the handler. I forgot about it, then spend some time wondering why my faults were not being propagated and caught properly on the client. So make sure you supply a well known action for your fault in the contract attribute and in the error handler like this:
First on your FaultContract attribute:
Then in your ProvideFault implementation:
So finally, thats it. Don't forget about matching up those actions, otherwise you'll waste your time.
Hope this has proved helpful and informative.
Update: The code for this article is here