NancyFx Tutorial



Hi, my teams recently been using NancyFx for a nice little project recently and I thought it might be nice to write a quick tutorial to help others get started. In this tutorial we'll be putting together a very simple RESTish api based around car classifieds. It will be strictly API no html view engines here (though Nancy can very much do that). Shameless plug I've made a NancyFx screencast video tutorial as well and a now a NancyFx testing tutorial for those wondering how easy it is to test Nancy.

What Is NancyFx

It's another web framework but it seems to take a slightly different approach to MVC and feels reasonably simple compared to the other frameworks out there. It makes heavy use of dynamic and other C# 4 features. Not to mention it makes extensive use of lambda's and all things new and shiny in C#.

It's based on or maybe more inspired by Sinatra in Ruby (get it Nancy Sinatra) and has an aim of not having any dependencies on System.Web. So this makes it ideally suited for running on Mono though I haven't tried it yet.

Before We Start

Download this tutorials code from Github
I've also made a NancyFx screencast or video tutorial if you don't feel like reading through this tutorial.

Step 1 - New Web Application

Firstly just create a new ASP.NET Web Application and be sure to select .NET Framework 4 as Nancy needs it. To be strictly honest it doesn't have to be a ASP.Net Application but for this tutorial we're going to be hosting it on ASP.Net. But Nancy can self-host and offers far more options than I'm going to mention here.

Step 2 - Delete Everything

We don't need all the default stuff (which I believe vs2012 gets rid of)* mental note to upgrade soon. You just need a bare project.

Next use the power of NuGet to add the Nancy package and also the Nancy.Hosting.Aspnet package.

If you look at the picture above you can see that Nancy is very modular with different packages for different things, including the Nancy.Hosting.Self stuff that I mentioned earlier.

While we're here we might as well add the Nancy.BootStrappers.StructureMap package so we can use a container we all know and love*.

Step 3 - Marvel at the Web.config

We're going to start off with an incredibly module that just exposes a /status endpoint and returns "Hello World" as a string (none of that fancy xml or json stuff). But first I thought I'd show you the web.config

You don't get much cleaner than that. You'll see that the Aspnet Nancy hosting is just implemented as a classic Asp.net HttpHandler, nothing shocking or complicated there.

Step 4 - Status Endpoint on the CarModule

Getting back on track we create a class called CarModule that inherits from NancyModule. NancyModule is where all the magic happens, the base class exposes a series of properties that mirror the various different Http verbs, like GET , POST , PUT and DELETE.

using Nancy;

namespace NancyTutorial
{
    public class CarModule : NancyModule
    {
        public CarModule()
        {
            Get["/status"] = _ => "Hello World";
        } 
    }
}

Looking at the simple code example above, we've created a simple constructor for CarModule and we've created a simple route that is /status. The _ is simply a variable name that by convention means we don't care about it, which we don't because immediately to the right of our lambda expression we completely ignore it and simply return 'Hello World'. The _ could of equally be named parameters or any other variable name.

So what is the Get well it's how Nancy does routing (which I personally find a little easier than Asp.Net MVC). It's a property on the NancyModule base class that takes in our example a string that is the path eg /status we can treat as a key and our value is a Func that is passed a dynamic parameter and returns a dynamic type. So in our example we call the dynamic parameters _ and we return a string "Hello World" as a dynamic.

It might look a bit weird but you get used to it and it's not really that magical. If we run our application and browse to /status we'll see that it returns "Hello World".

Step 5 - Passing Parameters using the Dynamic Dictionary

That was easy so now we want to use a parameter that's passed in as part of the url. So for example when someone does a GET /car/123 we want to get the 123 as a parameter into our method or Lambda in this case. If we look at the code below.

using Nancy;

namespace NancyTutorial
{
    public class CarModule : NancyModule
    {
        public CarModule()
        {
            Get["/status"] = _ => "Hello World";

            Get["/car/{id}"] = parameters =>
                                   {
                                       int id = parameters.id;

                                       return Negotiate
                                           .WithStatusCode(HttpStatusCode.OK)
                                           .WithModel(id);
                                   };
        } 
    }
}

 

We see we've created a route that matches the path /car/123 we've used similar tokenization for the id to that in traditional MVC. The {name} denote a parameter and Nancy works it's magic and puts all the values on a dynamic dictionary which this time we call parameters (last time it was _ ). Since it's a dynamic dictionary we can simply access the parameters.id property and then cast it to an int.

We then use another handy property from the NancyModule base class which is Negotiate. This is a handy Fluent style builder for constructing our response. In this case we make sure we return the 200 OK statuscode and then in the body of the response or .WithModel(id) we return the simple number.

Step 6 - Simple Model Binding

We can also use Nancy's simple model binding to autmatically bind url route parameters to properties of a custom type.

Get["/{make}/{model}"] = parameters =>
                                         {
                                             var carQuery = this.Bind<BrowseCarQuery>();

 

Above we simply call this.Bind<BrowseCarQuery>() which is another handy method on the base NancyModule which performs some simple convention based mapping. In this example Nancy maps the make and model parameters from the dynamic dictionary mentioned above to the identically names properties on the BrowseCarQuery class.

public class BrowseCarQuery
    {
        public string Make { get; set; }
        public string Model { get; set; }
    }

Step 7 - Returning some Json

Extending the above code slightly once we've used the nancy model binding and got our BrowseCarQuery then we simply create a list of Car objects in a list and return them. Conveniently Nancy is smart enough to autmatically serialise them into Json for us, so no hard work there.

Get["/{make}/{model}"] = parameters =>
                                         {
                                             var carQuery = this.Bind<BrowseCarQuery>();

                                             var listOfCars = new List
                                                                  {
                                                                      new Car
                                                                          {
                                                                              Id = 1,
                                                                              Make = carQuery.Make,
                                                                              Model = carQuery.Model
                                                                          },
                                                                      new Car
                                                                          {
                                                                              Id = 2,
                                                                              Make = carQuery.Make,
                                                                              Model = carQuery.Model
                                                                          },
                                                                      new Car
                                                                          {
                                                                              Id = 3,
                                                                              Make = carQuery.Make,
                                                                              Model = carQuery.Model
                                                                          }
                                                                  };
                                                                  

                                             return Negotiate
                                                 .WithStatusCode(HttpStatusCode.OK)
                                                 .WithModel(listOfCars.ToArray());
                                         };

Using the handy Chrome plugin Dev HTTP Client I poked the service and got the results shown below.

Step 8 - Handling Errors with Nancy

All good so far, but how do you handle errors, what about if when a user hits the endpoint /car/321 where 321 is a car id that does not exist we should throw a CarNotFoundException which should in turn return a 404 Not Found http status code.

 

Get["/car/{id}"] = parameters =>
                                   {
                                       int id = parameters.id;

                                       if (id == 321)
                                           throw new CarNotFoundException();

                                       return Negotiate
                                           .WithStatusCode(HttpStatusCode.OK)
                                           .WithModel(id);
                                   };

So if we look at the code again you'll see this small change that throws an CarNotFoundException if the id is 321. When we perform a GET /car/321 we get the default Nancy Internal Server Error page. Unfortunately that's not what we're after, we'd much rather a 404 Not Found error. To do that we need to create a new class that inherits from the StructureMapNancyBootstrapper.

using System.Text;
using Nancy;
using Nancy.Bootstrappers.StructureMap;

namespace NancyFx
{
    public class NancyBootstrapper : StructureMapNancyBootstrapper
    {
        protected override void ConfigureApplicationContainer(StructureMap.IContainer existingContainer)
        {
            StructureMapContainer.Configure(existingContainer);
        }

        protected override void ApplicationStartup(StructureMap.IContainer container, Nancy.Bootstrapper.IPipelines pipelines)
        {
            pipelines.OnError += (context, exception) =>
                                     {
                                         if (exception is CarNotFoundException)
                                             return new Response
                                                        {
                                                            StatusCode = HttpStatusCode.NotFound,
                                                            ContentType = "text/html",
                                                            Contents = (stream) =>
                                                                           {
                                                                               var errorMessage =
                                                                                   Encoding.UTF8.GetBytes(
                                                                                       exception.Message);
                                                                               stream.Write(errorMessage, 0,
                                                                                            errorMessage.Length);
                                                                           }
                                                        };

                                         return HttpStatusCode.InternalServerError;
                                     };
        }
    }
}

 

I should point out that if we weren't intending to use StructureMap a little later on we could just as easily inherit from the DefaultNancyBootstrapper. In the code above the bit to pay attention to is where we override the ApplicationStartup. We then subscribe to the OnError event expecting a NancyContext and the exception that was thrown.

We can then write a bit of somewhat dubious code that checks whether the exception is of the type CarNotFoundException. If it is we then return a Response setting the StatusCode to NotFound and then just write the Exceptions error message to the Contents output stream. That's all I'm going to do, though I'd recommend do a bit more work to return more informative not to mention json / xml friendly responses.

Step 9 - IOC with StructureMap

Next lets use our own inversion of control container, because we're more familiar with it.

using StructureMap;

namespace NancyTutorial
{
    public static class StructureMapContainer
    {
        public static void Configure(IContainer container)
        {
            container.Configure(config => config.Scan(c =>
            {
                c.TheCallingAssembly();
                c.WithDefaultConventions();
            }));
        }
    }
}

The code above is nothing particularly special, we're just looking for implementation within the calling type use default conventions. What we do need to do though is tell Nancy to use our container settings.

 

public class NancyBootstrapper : StructureMapNancyBootstrapper
    {
        protected override void ConfigureApplicationContainer(StructureMap.IContainer existingContainer)
        {
            StructureMapContainer.Configure(existingContainer);
        }
}

We've already seen this code above, but here it the basics again just to reiterate. Having done this we can move some of the logic from our CarModule into a CarRepository and have StructureMap inject the repository into our CarModule constructor. Here's a more complete code listing* Note the Bootsrapper code is not included as is the same as above.

using System;
using System.Collections.Generic;
using Nancy;
using Nancy.ModelBinding;

namespace NancyTutorial
{
    public class CarModule : NancyModule
    {
        private readonly ICarRepository _repository;

        public CarModule(ICarRepository repository)
        {
            _repository = repository;
            Get["/status"] = _ => "Hello World";

            Get["/car/{id}"] = parameters =>
                                   {
                                       int id = parameters.id;

                                       var carModel = _repository.GetById(id);                                    

                                       return Negotiate
                                           .WithStatusCode(HttpStatusCode.OK)
                                           .WithModel(carModel);
                                   };

            Get["/{make}/{model}"] = parameters =>
                                         {
                                             var carQuery = this.Bind<BrowseCarQuery>();

                                             var listOfCars = _repository.GetListOf(carQuery);                                                                                                          

                                             return Negotiate
                                                 .WithStatusCode(HttpStatusCode.OK)
                                                 .WithModel(listOfCars);
                                         };
        } 
    }

    public interface ICarRepository
    {
        Car GetById(int id);
        IList GetListOf(BrowseCarQuery carQuery);
    }

    public class CarRepository : ICarRepository
    {
        public Car GetById(int id)
        {
            if (id == 321)
                throw new CarNotFoundException();
            return new Car
                       {
                           Id = id,
                           Make = "Tesla",
                           Model = "Model S"
                       };
        }

        public IList GetListOf(BrowseCarQuery carQuery)
        {
            return new List
                       {
                           new Car
                               {
                                   Id = 1,
                                   Make = carQuery.Make,
                                   Model = carQuery.Model
                               },
                           new Car
                               {
                                   Id = 2,
                                   Make = carQuery.Make,
                                   Model = carQuery.Model
                               },
                           new Car
                               {
                                   Id = 3,
                                   Make = carQuery.Make,
                                   Model = carQuery.Model
                               }
                       };
        }
    }

    public class CarNotFoundException : Exception
    {
    }

    public class Car
    {
        public int Id { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
    }

    public class BrowseCarQuery
    {
        public string Make { get; set; }
        public string Model { get; set; }
    }
}

Conclusion

That's it for now. As you can probably see there's plenty of room for improvement on this code but I hope it gives a brief overview. I've put this NancyTutorial code on GitHub for your convenience. Good luck using Nancy.

References

I'd highly recommend the NancyFx website the Nancy documentation is pretty good. I'd also have a listen to this Hanselminutes Podcast on NancyFx for a good overview of Nancy. Another shameless plug would be to have a quick read of my testing Nancy tutorial to learn a bit about testing Nancy.


blog comments powered by Disqus

Search

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2014