Sunday 27 November 2011

WCF Custom Error Handling


One of the requirements of a recent project I was involved in required custom WCF error handling. The project used WCF REST services via HTTP Post and the requirement was that the request and response was in a JSON format message. If the WCF REST service encountered an error it would respond with JSON formatted error message plus HTTP Header Status Code of System.Net.HttpStatusCode.InternalServerError.
I had to basically create an error handler attribute class that would attach itself to the WCF Dispatchers. This class would inherit from attribute and IServiceBehavior.
You must add the error extensions before the first call arrives to the service and yet after the collection of dispatchers is constructed by the host. This narrow window of opportunity exists after the host is initialized but not yet opened.
To act in that window the best solution is to treat error extension as custom service behaviors because the behaviors are given the opportunity to interact with the dispatchers at just the right time.
public interface IServiceBehavior
{
   void AddBindingParameters(ServiceDescription description,
                             ServiceHostBase host,
                             Collection<ServiceEndpoint> endpoints,
                             BindingParameterCollection parameters);
 
   void ApplyDispatchBehavior(ServiceDescription description,
                              ServiceHostBase host);
 
   void Validate(ServiceDescription description,ServiceHostBase host);
}

The code below is a class that implements both IServiceBehavior and attaches the errorHandlerType as an attribute definition. This will be shown later in the class creation of the WCF.
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;

using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

    public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
    {
        #region "Error Handling"

        Type errorHandlerType;

        public ServiceErrorBehaviourAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
            errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);

            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }

Then for Type errorHandlerType; you will need to create a class derive it from IErrorHandler and implement the methods
        public bool HandleError(Exception error)

        public void ProvideFault(Exception error, MessageVersion ver, ref Message fault)

This is required for your own error-handling extension, hence you need to provide the dispatchers with an implementation of the IErrorHandler interface defined above.


    public class GetPricesErrorHandler : IErrorHandler
    {

        public bool HandleError(Exception error)
        {
            try
            {
// You can log the Error here.....
            }
            catch { }
            return true;
        }

        public void ProvideFault(Exception error, MessageVersion ver, ref Message fault)
        {
            try
            {
// Set the HTTP Response Status Code.....
                    WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.InternalServerError;
// Set the HTTP Response Content Type.....

                    WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
                    WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

// Setup the JSON Data Type for Response.....
                    Detail dtError = new Detail();
                    dtError.ErrorDescription = String.Format("ERROR_160");

//JSON Serialize the Data Type for Response.....

                    DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(Detail));

                    Message msgFault = Message.CreateMessage(ver, "*", dtError, jsonSerializer);

                    msgFault.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Json));

                    fault = msgFault;
            }
            catch { }
        }
    }

Then on the implementation of the WCF Rest Service.
We have access to the above 2 classes name ServiceErrorBehaviour and GetPricesErrorHandler
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
    [ServiceErrorBehaviour(typeof(GetPricesErrorHandler))]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class GetPricesService : IGetPrices

There you have it. Customised Error Handling for your WCF REST Implementation.

No comments:

Post a Comment