This post is over 6 months old. Some details, especially technical, may have changed.

Micro Web Frameworks in .NET 101: Nancy

Nancy is a different beast to Jessica while on the surface it may appear like a micro web framework, once you scratch the surface it becomes debatable because there is just some much more than a nice DSL for creating lightweight web apps. In fact this was one of the reasons Jessica was created,

Nancy has taken an approach to making many parts of the framework replaceable; if you don?t like the functionality of X you can create your own, and have Nancy use that instead. While this is a respectable goal for a web framework, the focus for Jessica has been to stay as simple and as close to Sinatra as possible. Tom Bell. Why Did I Create Jessica?

Anyway we will touch on these aspects a bit later. Lets get cracking.

Getting Started - Hello World

Getting Nancy up and running is simple enough. Starting with an empty ASP.NET web project1 we can use NuGet to install Nancy

Install-Package Nancy

Also because we are hosting this in an ASP.NET environment (more on this later) we need to install the appropriate hosting package

Install-Package Nancy.Hosting.Aspnet

Next step requires us to add some stuff to the web.config. If you've been smart and installed the hosting package from NuGet this steps is done for you but ultimately your minimal web.config should look like this.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <httpHandlers>
      <add verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" path="*" />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <add name="Nancy" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" path="*" />
    </handlers>
  </system.webServer>
</configuration>

The final step is to create the hello world module that will handle your route. Again, like Jessica, it doesn't matter where the module classes are placed but for neatness I just like to stick them in a Modules folder. Our module will look very similar to the one we defined for Jessica.

public class HelloWorldModule : NancyModule
{
    public HelloWorldModule()
    {
        Get["/"] = r => "Hello World";
    }
}

And we're done. Fire the app up and behold the "Hello World"-yness of it all.

Other Micro Framework Related Stuff

Nancy has some features that aren't technically related to the micro framework aspect - specifically hosting but I'll touch on that later. Nancy also has a lot of nice features that are related to the micro framework aspects so lets take a bit of a whirlwind tour around the main areas first.

Routes

Routes in Nancy are pretty powerful. Obviously you get the declare static routes (as demonstrated above) but dynamic routes (routes with variables for example) are also supported in a few of ways. First of all you have named segments. These can be expressed in 2 ways

  1. Sinatra style variables /user/:id, or,
  2. C#-esque String.format style /user/{id}

Declaring a route like this will cause the variable section to be pushed into a named variable within the dynamic object passedin into the route action. /user/jameshu matches /user/:id and the dynamic object passed into the action will have x.id == "jameshu"

The other means of dynamic route matching is the use of regular expressions and named capture groups (or backreferences as they are sometimes known). Using regular expressions as route parameters allows us to offer more fine grained route matching. Take the following route as an example,

Get[@"/users/(?<id>[\d]+)"] = r => GetUser(r.id);

This route will ONLY match routes whose variable section matches the expression, in this case a numeric value. So not only have we specified a route variable but we are also limiting access to that action to "valid" values. This means we could also offer another route that accepts only alphabetical characters. Powerful enough.

Route Conditions

Taking routes another step further Nancy also offers optional conditions that can be used to determine if a route should be executed for a given request. Lets use this heavily contrived example to demonstrate this feature,

Get["/user", ctx => ctx.Request.Query["password"] == "secrets"] = r =>  "Hello World";

So this route will match any route starting with /user however the condition (the second argument of the definition) will ensure that the route action will only get executed if the query string is populated with an argument called password with a value of secrets i.e http://myserver/user?password=secrets will match successfully.

Views

Nancy supports a decent range of view engines2

  • Razor
  • Spark
  • NDjango and, obviously,
  • Static files

Serving up dynamic vies is fairly simple

public class HelloWorldModule : NancyModule
{
    public HelloWorldModule()
    {
        Get["/"] = r => View["index.cshtml", "Hello World"];
    }
}

We basically use the View object to specify the view and optionally pass in a view model as well. By default Nancy will look in the ~/Views folder for the views but you can also specify a full path if needs be. The view is then matched against the available view engines and rendered as you would expect. This is the Razor view specified above.

<!DOCTYPE html>
<html>
    <head>
        <title>Hello World</title>
    </head>
    <body>
        <h1>@Model</h1>
    </body>
</html>

Responses

Nancy is also capable of returning static content as different responses (similar to how Jessica does it). Each route action is expected to return a Nancy Response object. The object itself has a number of convenience methods for serving static files and JSON such as Response.AsJs, Response.AsJson, Response.AsXml, , Response.AsRedirect etc.

Hosting

This is were things move away from "micro framework" world a bit. Nancy itself abstracts out the hosting environment making the entire framework portable allowing you to host in any number of places including (but not limited to),

  • Self hosted (e.g. embedded within an executable)
  • ASP.NET/IIS (as demonstrated above)
  • WCF (yep thats right), and,
  • Some partial OWIN support

This, to me at least, is very interesting. Being able to build an executable that accepts HTTP requests could be pretty powerful in the right hands and the WCF stuff sounds very interesting (is this similar to what the new WCF Web API is attempting to offer?). I need to play some more in this area. I'll report back when I am done.

Summing Up

So that was a quick round up of the key features of Nancy and I think it's another one worth looking at. So hopefully you can also see how it differs from Jessica. From a code perspective they seem quite similar (though Nancy has possibly a few more features) but the whole hosting abstraction layer is very interesting. I have had people ask me if this introduced any new (or even old) security concerns and to be honest I don't know but it might certainly be worth looking out for.

As always I may need corrections so please fire away. Hopefully I can cover off these points with a few more screencasts over the next week. Stay tuned.

1 In my Jessica I laid out steps to strip the normal ASP.NET Web Project template out of all the unnecessary bits. Lord knows why I didn't just create an empty web project <facepalm/>

2 Apparently it also has a built in view engine called the Super Simple View Engine but as of yet I can't seem to get it to work.

Published in .NET on May 22, 2011