Apr 7 2008

Implementing IErrorHandler

Category: .NetRory Primrose @ 11:08

As soon as I read about IErrorHandler in Juval Lowy's book, I was sold. This interface in WCF is excellent to use for error handling and shielding at service boundaries. The interface comes with two methods.

  1. ProvideFault allows you to handle the error and determine what is returned to the client. This is where exception shielding comes in. The service implementation or any of the layers in the service can throw an exception. The error handler is the opportunity to determine whether the exception thrown is something that the service understands and is happy for the client to receive or whether the exception needs to be shielded into another exception/fault. A shielded exception would typically be a generic service exception that says that the service encountered an error.
  2. HandleError allows you to process some error specific logic asynchronous to the service call. This means that you could do some tracing/instrumentation or some expensive operation without blocking the client call.

There issue that I have with IErrorHandler is that the documentation is not detailed enough to allow you to hook up an error handler so that it is actually used. My recent experience has resulted in me defining an IErrorHandler, but not correctly configuring it so that it gets invoked. This highlights a disadvantage of the IErrorHandler design (assuming a configuration based setup). The service will still work without the handler being invoked, you will just get different exceptions on the client. This has a potential security risk if exception shielding has been written to shield sensitive information from clients.

The MSDN documentation (see here) provides examples about how to to create an IErrorHandler implementation including the behavior extension, but doesn't provide the full class examples. Hence my misunderstanding that resulted in my handler not being invoked. I suggest that two classes get created. One is the error handler, the other is the error handler element for configuration.

ErrorHandler

The error handler implements IErrorHandler, but also implements IServiceBehavior. This interface allows the error handler to be hooked up by configuration.

using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
 
namespace WcfServiceLibrary1
{
    public class ErrorHandler : IErrorHandler, IServiceBehavior
    {
        public void AddBindingParameters(
            ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase,
            Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {
        }
 
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler = new ErrorHandler();
 
            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
 
                if (channelDispatcher != null)
                {
                    channelDispatcher.ErrorHandlers.Add(errorHandler);
                }
            }
        }
 
        public bool HandleError(Exception error)
        {
            Trace.TraceError(error.ToString());
 
            // Returning true indicates you performed your behavior.
            return true;
        }
 
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
 
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            // Shield the unknown exception
            FaultException faultException = new FaultException(
                "Server error encountered. All details have been logged.");
            MessageFault messageFault = faultException.CreateMessageFault();
 
            fault = Message.CreateMessage(version, messageFault, faultException.Action);
        }
    }
}

ErrorHandlerElement

The error handler element defines the extension behavior such that ErrorHandler can be defined against a service behavior.

using System;
using System.ServiceModel.Configuration;
 
namespace WcfServiceLibrary1
{
    public class ErrorHandlerElement : BehaviorExtensionElement
    {
        protected override object CreateBehavior()
        {
            return new ErrorHandler();
        }
 
        public override Type BehaviorType
        {
            get
            {
                return typeof(ErrorHandler);
            }
        }
    }
}

Configuration

This configuration identifies the ErrorHandlerElement as a behavior extension, which then allows errorHandler (the name of the configured extension) to be defined against the service behavior. This is how the error handler gets hooked up for the service.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
  <system.web>
    <compilation debug="true" />
  </system.web>
 
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <errorHandler />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="errorHandler"
            type="WcfServiceLibrary1.ErrorHandlerElement, WcfServiceLibrary1" />
      </behaviorExtensions>
    </extensions>
    <services>
      <service behaviorConfiguration="MyServiceBehavior"
              name="WcfServiceLibrary1.Service1">
        <endpoint binding="wsHttpBinding"
                  contract="WcfServiceLibrary1.IService1" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

This will hook up the error handler to cover any exceptions thrown by the service. Error handlers can also be debugged via the IDE.

Tags: , , ,

Comments

1.
David Jansen David Jansen says:

I have an issue with the configuration when specifying the type for the  behavior extension. When specifying the type, I must provide the fully qualified type, otherwise WCF throws a CommunicationObjectFaultedException.

I dont want to have to specify a fully qualified type as the version number of my ErrorHandler assembly changes for each continous build.

Any suggestions?

2.
Rory Primrose Rory Primrose says:

Hmm, I will have to play with this one a bit.

From doing some Reflector research, it looks like the xml configuration element is being read in System.ServiceModel.Configuration.ExtensionElementCollection.EnforceUniqueElement(ExtensionElement element) where it makes the following call:

Type type = Type.GetType(element.Type, false);

element.Type indicated is the string value in the type attribute in the config. This means that the intepretation of the type value is based on the functionality of Type.GetType.

My only suggestion is to define the type using "typename, assembly" and then look at the logs of fuslogvw.exe. This might give you a hint as to why the type fails to resolve without being fully qualified.

3.
Roy Roy United States says:

Using VS 2008 SP1 Professional, I copied and pasted your code, exactly as you offered it, into a WCF project. In the web.config, the <errorHandler /> tag is underscored in blue:

<behavior name="MyServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
<errorHandler />

Hovering over it yields the following message:

The element 'behavior' has invalid child element 'errorhandler'. List of possible elements expected:
'serviceAuthorization, serviceCredentials, serviceMetadata, serviceSecurityAudit, serviceThrottling,
dataContractSerializer, serviceDebug, serviceTimeouts, persistenceProvider, workflowRuntime'.


When I tried to create a service reference, I received the following:

[ArgumentException]: Extension element 'errorHandler' cannot be added to this element.  Verify that the extension is registered in the extension collection at system.serviceModel/extensions/behaviorExtensions.

In fact, EVERY sample I tried from the Internet that uses this approach results in the same error.

Any ideas on what's wrong?

4.
negasonic negasonic United Kingdom says:

I see a couple of other people have also found this code breaks.  For resolution take a look at:

nayyeri.net/.../

5.
pingback tuts9.com says:

Pingback from tuts9.com

Implementing IErrorHandler using class level Attributes in WCF | The Largest Forum Archive

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading