mandag den 11. juli 2011

HTTP status code in WCF using error handling

While the Web.API available on Codeplex in many ways seems like an excellent framework for building Rest-full webservices on top of WCF and .NET I recently tried to dive into the plain WCF-framework and look at the extension points available with the purpose of building a rest service directly on top of WCF.

One of the interesting and simple possibilites is to use standard WCF error handling to capture exceptions and map them to http status types. Of course this should only be a part of the exception strategy, but it is certainly usefull and makes it possible to avoid a lot of repetitive try-catch blocks. This in it self is naturally a cool feature and useful whether or not the service being implemented is aimin for rest or any other component.

The building blocks are as follows:

  • An IErrorHandler implementation that takes care of catching exceptions and perform the wanted logic operations.
  • A service behavior which injects the error handler into the WCF call stack. (Note that while we are only discussing one single error handler it would be possible to create operation behaviors and bind the exception logic by a method decorator instead.)
  • An attribute for decorating the service and binding the service behavior
  • Some kind of mapping between application exceptions and http status codes. 
Service behavior
The simplest possible example would be to map a common exception handler to all methods of a service. The error handler should be added to the ChannelDispatcher in service behavior.

This can be obtained with a class similar to the one below. It is simply an attribute that decorates the service implementation and injects an error handler of type MappingErrorHandler into the dispatcher:

namespace WcfErrorHandling
{
    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.Collections.ObjectModel;
    using System.ServiceModel.Channels;
 
    [AttributeUsage(AttributeTargets.Class)]
    public class HttpMappingErrorBehaviorAttribute
        : AttributeIServiceBehavior
    {
        public void AddBindingParameters(
            ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase, 
            Collection<ServiceEndpoint> endpoints, 
            BindingParameterCollection bindingParameters)
        {
            // No op
        }
 
        public void ApplyDispatchBehavior(
                          ServiceDescription serviceDescription, 
                          ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler = new MappingErrorHandler();
            foreach (ChannelDispatcherBase channelDispatcherBase 
                  in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = 
                    channelDispatcherBase as ChannelDispatcher;
               channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
 
        public void Validate(ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase)
        {
            // No op
        }
    }
}


The validate method should be implemented to make it more robust. In this example we assume everything is bound to the webHttpBinding so it would be wise to test if this binding was actually enabled, and of course other tests derived from the actual logics could be implemented.

IErrorHandler
Implementing the IErrorHandler interface is also very straight forward. The example below simply tests if an exception is a mapped type in a large yucky if statement and translates the two known types into http status codes. The boolean HandleError returns true if the exception is an ApplicationException; this is what this simple handler is designed to handle.


namespace WcfErrorHandling
{
    using System;
    using System.Net;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Web;
    using System.ServiceModel.Channels;

    
    public class MappingErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            return error is ApplicationException;
        }
 
        public void ProvideFault(
            Exception error, MessageVersion version
          , ref System.ServiceModel.Channels.Message fault)
        {
            if (error != null)
            {
                if (error is NotFoundException)
                {
                    SetStatus(HttpStatusCode.Conflict);
                }
                else if (error is ApplicationException)
                {
                    SetStatus(HttpStatusCode.Created);
                }
                else
                {
                    throw error;
                }
            }
        }
 
        private void SetStatus(HttpStatusCode status)
        {
            WebOperationContext.Current
             .OutgoingResponse.StatusCode = status;
            WebOperationContext.Current
             .OutgoingResponse.SuppressEntityBody = true;
        }
    }
}

Connecting to a service
To add the error handler to a service, we simply decorate the service implementation with the attribute. So assuming the service contract below

namespace WcfErrorHandling
{
    using System.ServiceModel;
    using System.ServiceModel.Web;
 
    [ServiceContract]
    public interface IExceptionThrower
    {
        [OperationContract]
        [WebGet(UriTemplate = "/hello")]
        string Hello();
 
        [OperationContract]
        [WebGet(UriTemplate="/AppError")]
        void AppError();
 
        [OperationContract]
        [WebGet(UriTemplate="/NotFound")]
        void NotFound();
    }
}


with an implementation

namespace WcfErrorHandling
{
    using System;
 
    [HttpMappingErrorBehavior]
    public class UniformCatch : IExceptionThrower
    {
        public void AppError()
        {
            throw new ApplicationException("App exception ocurred.");
        }
 
        public void NotFound()
        {
            throw new NotFoundException();
        }
 
        public string Hello()
        {
            throw new ApplicationException();
        }
    }
}

will cause all requests to be translated to status codes as described in the exception mapper.

mandag den 4. april 2011

Optimization by Release build making nunit test fail?

A strange failure on the build server today: 


Given the test
        [Test]
        public void TestAcceptsDatetime()
        {
          Assert.That(validatorUnderTest.IsValid(DateTime.Now), Is.True);
        }
Which tests the method 

        public bool IsValid(object o)
        {
          return o is DateTime;
        }
I got a passing test when compiling in debug mode. But when submitted to the build server building and executing in release mode, the tests fails. 

The bastard defied manual debugging, since transforming the method being tested to produce a debug print also made the test pass. That is after making the following change
        public bool IsValid(object o)
        {
          Console.WriteLine("o is a " + o.GetType());
          return o is DateTime;
        }

made the test pass and indeed also produced the expected output "o is a System.DateTime".

To me the only reasonable explanation is that the compiler or runtime somehow optimizes away the type information on the object in the method without debug printing, since it sees that the object is not really used. The compiled version seemed to be behaving correctly. 

The test was made to pass by assigning the Datetime.Now to a value in the test. This seem to make the assumptions about the is operator correct, but it is a strange resolution. 

        [Test]
        public void TestAcceptsDatetime()
        {
          var testvalue = DateTime.Now;
 
          Assert.That(validatorUnderTest.IsValid(testvalue), Is.True);
        }

onsdag den 12. januar 2011

Missing [WebGet] on WCF auto generated client

I have been working on a WCF web service for a client for some time now. Today I added a new method to the existing service interface and left everything else untouched. After updating the service reference in VS2010 the proxy presented me with the exception


The service methods are decorated with [WebGet] and it is exposed using the webHttpBinding. Without being true REST it is intended for easy consumation across platform and hence not only WCF clients. 

System.InvalidOperationException : Operation 'active' of contract 'ISubscriptionService' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.

The strange thing is that the proxy simply needed to get the [WebGet] attribute assigned which caused the abovementioned error to dissapear. But why is this attribute not generated automatically?

Apparantly decoration with  [WebGet] does not happen automatically in the service proxy. But luckily it can be put there manually. Just need to remember this when the service reference is updated.