Introducing Nancy, a lightweight web framework inspired by Sinatra

November 28th, 2010

For a couple of weeks I have been keeping myself busy with open-source stuff. One of the things has been to spike out a web framework idea and later on turn it into a real project. The project is inspired, but not a clone, by the Sinatra web framework built on Ruby. The name, Nancy, is a reference to Nancy Sinatra, the daughter of Frank Sinatra.

There are quite of lot of things that I want to put into the framework, but it is functional in its current state. One of the goals for Nancy is to make it run on other environment and platforms, other than ASP.NET / IIS and there are spikes taking place to run it on Mono with FastCGI, making it possible to run on a bunch of other platforms. However, although this is the goal, the current source code does not provide any helpers to make that possible. Right now it only ships with an IHttpHandler that acts as an adaptor between ASP.NET / IIS and the Nancy engine.

The project is built using C# and makes use of xUnit, MSpec and FakeItEasy

The key component in a Nancy application is the modules. This is where you create actions, which are handlers for a given request type at a given path. Let me show you what I mean

public class Module : NancyModule
{
    public Module()
    {
        Get["/"] = parameters => {
            return "This is the site route";
        };

        Delete["/product/{id}"] = parameters => {
            return string.Concat("You requested that the following product should be deleted: ", parameters.id);
        };
    }
}

What you are looking at here is the foundation of a very small application that will responde to GET requests to the root URL of the site, and DELETE requests to /products/{id} where {id} is a parameter placeholder. All parameters will be captured and injected into the action, like you see with parameters.id.The entire route handling mechanism is swappable, so you could write your own handler that were able to interpreted the route syntax that you prefer. A module can also be declared with a module path, meaning that all action routes, that you declare in the module, will be relative the module path.

For example if you were to do

public class Module : NancyModule
{
    public Module() : base("/foo")
    {
        Get["/"] = parameters => {
            return "This is the site route";
        };

        Delete["/product/{id}"] = parameters => {
            return string.Concat("You requested that the following product should be deleted: ", parameters.id);
        };
    }
}

The the application would respond to requests sent to /foo and /foo/product/{id}. You can of course have as many modules as you want. Nancy will detect them all and figure out which action that should be invoked. There are also some nice ticks in there for return values. In the examples above you get the impression that you are expected to return a string, and this is not the case. In fact each action returns an instance of a Response type. What you are seeing is the result of some implicit cast operators. There are a couple of them declared

public class Module : NancyModule
{
    public Module()
    {
        Get["/"] = parameters => {
            return "Returning a string";
        };

        Get["/404"] = parameters => {
            return 404;
        };

        Get["/500"] = parameters => {
            return HttpStatusCode.NotFound;
        };
    }
}

All of these will work and send back a valid HttpResponse (including headers) to the client. You can of course explicitly return a Response instance which opens up for some nice customization. A module in Nancy also declares a pair of properties called View and Response. Both of these have an interface return type and each of them are empty marker interfaces that you can use to wire up extension methods. The View property is meant to be used for view engine integration and in an unpublished spike (still needs some more work) I’ve wired up the http://www.sparkviewengine.com/ so that Nancy is able to process spark files. This is an example of what that looks like

public class SparkModule : NancyModule
{
    public SparkModule()
    {
        Get["/user/{name}"] = parameters => {
            return View.Spark("user.spark", parameters);
        };
    }
}

Of course all of this is work in progress and the syntax might change. The goal is to support all of the popular view engines and if you are up to the task of implementing support for one of those, please let me know – I would love the help!

The Response property is meant to be used for extensions that help format the response in different ways. A test I have running locally is an extension method that enables me to return json formatted data.

public class JsonModule : NancyModule
{
    public JsonModule()
    {
        Get["/user/{name}"] = parameters => {
            return Response.AsJson(someObject);
        };
    }
}

Of course I would like for Nancy to ship with a healthy set of these response helpers, so feel free to chip in!

Oh, there is one last property that you can use and that is the Request property which gives you access to the current request information, so that you can use it from inside of your action handlers. Right now it is limited to the requested path and verb, but the goal is to have a rich representation of the current request – what stuff would you like to see in it?

public class RequestModule : NancyModule
{
    public RequestModule()
    {
        Get["/"] = parameters => {
            return string.Format("You requested {0}", Request.Path);
        };
    }
}

One thing I would like to mention about the action handlers and their routes. If there are two or more routes that are a match to the current request, Nancy is going to select the one that has the most matching static path segments before a parameter placeholder is reached (but all segment has to be filled!). What does this mean? Take the following routes

/foo/{value}
/foo/bar/{value}

The first route has one static path segment (/foo) and the second one has two (/foo/bar). So for a request to

/foo/bar

The first route will be selected, but for

/foo/bar/baz

the second route will be selected. It also important to understand that in Nancy, all path parameters are greedy, not like in ASP.NET MVC where you can have one greedy (indicated by a star *) and has to be at the end. If you define a route

/foo/{value1}/bar/{value2}

you can invoke it with

/foo/this/is/some/segments/bar/and/then/some/more

and you will end up with

{value1} = /this/is/some/segments
{value2} = /and/then/some/more

Of course, like I said before, this is how the default route handler works and if you don’t like it you can write your own, all you have to do is implement a single interface and tell Nancy to use it.

So before I end this post, let me tell you about some of the things that are planned to be included in Nancy as soon as possible

  • A much richer request object. Nancy uses it’s own Request object and is not tied down the the one found in ASP.NET. I want to support a rich and easy to use model for request information. If you have any suggestions on the structure of this object, please let me know
  • The ability to inject dependencies into Nancy modules. I want you to be able to wire up Nancy to use your favorite IoC and have Nancy resolve constructor dependencies of Nancy modules
  • Conditions on actions. I want to add an optional predicate on actions like Get[“/foo”, () => somePredicate], to give Nancy the ability to select actionsat runtime. For example you might have two identical actions define, but you add a predicate on one of them that made sure that it was only selected if the client was a mobile device. Actions that has a predicate defines should have higher priority than those that do not
  • View engine integration. Like I said, the Nancy modules comes with the View property, which is of the type IViewEngine, where you can hook up view engine support. All you need is an adapter that returns a string (or a Response instance). Please let me know if you want to chip in and help wire up one or more view engines
  • Ship with a nice bunch of response formatters. These are created by attaching extension methods to the IResponseFormatter interface, which is the property type of the Response property on a Nancy module. I think the formatters should follow a naming convention where you name them As<something>
  • Self-composed framework. What is mean with this is that I want to build Nancy on top of a tiny, internal, IoC that is used to compose the framework at runtime. It should be exposed in a simple way so that you could swap out components (such as the route matcher, or module discovery mechanism) as you please
  • Request and Response interception. The idea is to provide a lightweight way to intercept Nancy requests before they hit the Nancy application and let you either pass the request to the Nancy application or prematurely send back a reply. Combines with the ability to intercept Responses sent by the Nancy application, it should give a nice way of extending Nancy with features like logging and caching. You can think of this as a sort of IHttpModule mechanism
  • NuGet presence
  • Command line (powershell?) support for spawning up a Nancy application project
  • Provide self-hosting somehow

There are a bunch of other stuff I have in my head, but I have to give them some more thought to distil proper ideas from them. But please, let me know if you can think of anything more! I want to keep Nancy lightweight and easy to use, so it will probably never be as open-ended as ASP.NET MVC, FubuMVC or Manos de Mano – but we’ll have to wait and see!

You can find the source code at my Nancy repository at GitHub. You can also reach me on Twitter at @TheCodeJunkie. If you want to talk about Nancy drop me a line in the comments or on Twitter and we can move onto e-mail, gtalk, skype or messenger if needed! I hope you like where Nancy is going!

  • Brian

    Cool. Can’t wait to hear more.

  • James

    Not sure where this project is, but I just stumbled over “Mercury” which shows how cool a really simplified “dsl” can drastically reduce all the noisy config code. Here is the project link:

    https://github.com/olsonjeffery/mercury

  • Anonymous

    Looks cool, will try use it in my project

  • universh

    The project is the result of a discussion with a friend were we had the same ideas on how stuff should work. In regards to a target audience… well I haven’t really profiled it yet, too early to say. That said, I have two full web based applications that I plan in trying to build on top of NancyI really want to dogfood it so that ideas that the things that I put into Nancy have been extracted from real business needs.