Friday, August 6, 2010

Exposing NServiceBus as a REST(WCF) Endpoint


I recently had a situation where I wanted to expose NSB as a REST endpoint.  WCF hosting comes out of the box with NSB, but it defaults to an HTTP binding.  Normally it would be a problem to just add another endpoint via config, but when using the webHttpBinding in WCF we need the ability to decorate operations with some attributes which is not configurable in a config file.  Luckily for us, NSB gives us a simple extension point to wire in our own ServiceHost.  I started by creating my WCF contract:

[ServiceContract]
    public interface IProductCreatedRestService
    {
        [OperationContract]
        [WebInvoke(UriTemplate = "products", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
        void Create(ProductCreatedMessage message);
    }
Next up is the concrete implementation:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class ProductCreatedRestService : IProductCreatedRestService
    {
        private readonly IBus bus;

        public ProductCreatedRestService()
        {
            if ( null == this.bus )
                this.bus = Configure.Instance.Builder.Build<IBus>();
        }

        public void Create(ProductCreatedMessage message)
        {
            this.bus.Send(message);
        }
    }

 The big note here is that I'm building an instance of IBus using the NSB IoC container. An alternative would be to register the component at startup and let NSB property-inject IBus. The next thing I need to do is create and open up a WebServiceHost for my service. I do this via the IWantToRunAtStartup marker interface. NSB makes heavy use of the marker interface pattern. This allows it to identify certain classes via the IoC container. This interface let's us plugin our own custom code when NSB starts up and subsequently shuts down. Here is my startup class:

 public class RestServiceStartup : IWantToRunAtStartup
    {
        private WebServiceHost host;

        public void Run()
        {
            this.host = new WebServiceHost(typeof(ProductCreatedRestService));
            this.host.Open();
        }

        public void Stop()
        {
            if (null != this.host && this.host.State == System.ServiceModel.CommunicationState.Opened)
                this.host.Close();
        }
    }
My handler for the message is standard issue NSB fare:

 public void Handle(ProductCreatedMessage message)
        {
            // Normally you would do a Bus.Send here first
            if (message.ProductNumber == 1111)
                this.Bus.Return((Int32)CommandErrorCodes.Fail);
            else
                this.Bus.Return((Int32)CommandErrorCodes.Success);
        }
Now we can fire up our NSB instance and also jump into Fiddler to generate our request:





2 comments:

  1. Hi there, Did you put this REST based service within the NServiceBus Host Project (endpoint) or in a separated project? Can they belong at the same project?

    ReplyDelete