Update 02/02/10:
Please note the version displayed here doesn't reflect the version published to CodePlex, there were some changes and the behavior extension element was removed for the time being.
Check the Show Cases at CodePlex for code samples.
Update 24/03/09:
The attachment was updated with a new version. (Fixed a small bug)
The built-in extensibility point for handling errors is by implementing “IErrorHandler” interface and hooking it up into the channel dispatchers
public interface IErrorHandler
{
bool HandleError(Exception error);
void ProvideFault(Exception error, MessageVersion version, ref Message fault);
}
- ProvideFault allows you to provide an alternative message fault to be returned to the client but this isn’t what the post is about.
- HandleError is the place where you would write your error handling logic, it usually ends up with some sort of logging. (whether it’s writing to a file / database / mail and so on)
My goal was to log much more than just the actual exception, to be specific, I was aiming for the following:
The client’s identity, the parameters sent to the service operation and the entire request message envelope.
Unfortunately, WCF doesn’t give you built-in access to fetch the last 2 in the scope of the ‘HandleError’ method.
You can access the OperationContext.Current in that scope to extract more information, I will address each of the subjects mentioned above:
- The client’s identity
You can access the service security context attached to the operation context and extract the identity information from there.
Oddly enough, in some case where there would be no context available, trying to access the security context will throw an ‘ObjectDisposedException’ so you need to handle this accordingly.
- The input parameters
There is no built-in way to access this information from this scope
- The request message
You can access the request message on the request context. However, it is in a closed state so you can’t really do much with it.
So that is the problem at hand..
Luckily, WCF is such an extensible and pluggable framework, I love it :)
I wrote more behaviors, hooked up into the WCF pipeline in the right places and made this work.
Feel free to download the attached project to see this in action.
I wrapped the solution in a way that it could be easily used, let’s take a look at it:
First, you must get acquainted with your custom implementation point:
public class ErrorDetails
{
public ServiceSecurityContext SecurityContext { get; }
public Message RequestMessage { get; }
public object[] Parameters { get; }
}
public interface IErrorDetailsHandler
{
void HandleError(Exception error, ErrorDetails details);
}
As you can see, there is a new interface in town - “IErrorDetailsHandler”.
This should be your custom implementation, no need to implement IErrorHandler on your own. (Unless you wish to do something in the ProvideFault method)
As you might have guessed, I handled the security context ‘ObjectFaultedException’ occurence under the covers so you don’t need to worry about it.
Here is a really dummy class that implements this behavior:
namespace MyServices
{
public class MyErrorDetailsHandler : IErrorDetailsHandler
{
public void HandleError(Exception exception, ErrorDetails details)
{
//handle the error as you see fit..
}
}
}
Now all that is left is to plug our details handler into the WCF using my infrastructure.
I’ve enabled several ways to do that – behavior / attribute / configuration
Let’s look at all 3 ways, pick the one which fits your needs the most.
- Plugging the behavior into the service description
Once we create the service host, we can insert the behavior before opening it, as follows:
myHost.Description.Behaviors.Add(
new ServiceErrorHandlerBehavior("MyServices.MyErrorDetailsHandler, MyServices", true));
- Using a service-level attribute
[ServiceErrorHandler("MyServices.MyErrorDetailsHandler, MyServices", true)]
class Service : IService
{ … }
-
Using the configuration
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="ErrorHandler">
<errorHandlerExtension createMessageBuffer="true"
errorDetailsHandlerTypeName="MyServices.MyErrorDetailsHandler, MyServices" />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="errorHandlerExtension"
type="WcfContrib.Errors.ServiceErrorHandlerBehaviorExtensionElement, WcfContrib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=242f357400093587" />
</behaviorExtensions>
</extensions>
<services>
<service name="MyService" behaviorConfiguration="ErrorHandler">
<endpoint address="<address>"
contract="<contract>"
binding="basicHttpBinding" />
</service>
</services>
</system.serviceModel>
The last thing left to discuss are the parameters the behavior receives -
- ErrorDetailsHandlerTypeName (string)
The type name of your custom error details class – this is required.
Note that the class needs to have a public parameter-less constructor.
- CreateMessageBuffer (bool)
Indicates whether you need to access the request message, this isn’t required.
This defaults to false! This is because it creates a copy of the original request so you could access it later on and such processing shouldn’t be done unless you actually need it.
So that is it.. you can now easily access and log this information which could be of great assistance.
Note that this is one of the features of a future project I plan to release to CodePlex, so keep your eyes and ears open :)
To download the attached project,make sure you enter the post specific page.
CodeProject
Published
Monday, March 23, 2009 12:37 AM
by
Amir Zuker