close icon
.NET

Meet Nancy, a Lightweight Web Framework for .NET

Learn about Nancy, an open-source and lightweight alternative to the ASP.NET MVC framework.

October 17, 2018

TL;DR: In this article, you will learn about Nancy, an open-source alternative to the ASP.NET MVC framework. For starters, you will learn about Nancy's history, then you will scratch the surface of why you need another framework, then you will see it in action by creating a super-simple web server with it. If you need, you can find the code developed throughout this article in this GitHub repository.

"Meet Nancy, a mature and open-source alternative to the ASP.NET MVC framework."

Tweet

Tweet This

Nancy for a new generation

Let's talk about Nancy, the .NET web framework, named after a famous singer's daughter, curious?

By saying the words "Web Framework" to you, I'm guessing it provokes quite a bunch of different reactions from "Oh goodness, not another one..." to "Please, enough already..."

Web frameworks are a dime a dozen these days, and it seems like everyone is writing a new one, there's so much saturation these days, that you're well placed to groan with contempt at the mention of yet another framework.

The thing is though, the framework you are about to learn is not really a framework in the same domain that Angular, Aurelia, Vue.js, and React are. This is an open-source backend framework for .NET and it's been around for a surprisingly long time.

So what about Nancy?

Nancy might seem at first to be an unusual name for a web framework, but once you start to read the introductory info in the Wiki, you'll soon find out that it's modeled on the Ruby web framework "Sinatra", and Frank Sinatra's daughter was called, yes you guessed it… Nancy.

But why do you need a framework on the backend?

Web frameworks are not all about the client side, you still need to perform routing and parameter passing from your client-side code, and into your backend code so that you can then use these inputs to perform business logic and the outputs to pass information back to the client for display.

Having a toolkit like Nancy greatly helps with this task, just as Angular and Aurelia help with creating the front end and user interface.

In .NET (you will be using C# in this article) there is realistically only 2 major ways provided by default for creating a web application, the first is through the use of "Web Forms", Microsoft's original web framework modeled closely on the desktop framework "Win Forms", the second is the newer and more flexible ASP.NET MVC framework, designed to implement application design using the commonly known "Model, View, Controller" architecture pattern.

This left a lot of space in the backend framework market for a number of different toolkits, designed to produce HTTP based APIs. Nancy was one of the first, along with FubuMVC, OpenRasta, and many others.

The first official blog post at blog.nancyfx.org was written way back in 2011, and the first commit was checked into GitHub a year earlier in November 2010.

The 2 main originators of the framework, Andreas Håkansson and Steven Robbins, are still very much still major contributors to the project today in 2018, but now with a huge following and dedicated community behind them.

Is Nancy still appropriate in software development today?

That's the good thing: yes it is, and very much so.

Nancy is now a stable, mature, and very well supported set of tools for creating first-class HTTP based interfaces for all manner of projects.

What do I mean by this? Well, stop to think for a moment... Web-based application, doesn't have to mean "Web Server based application".

A lot of people assume that if you're building a web application, that it has to use HTML & CSS in the browser, and that it has to have a web server like IIS, Nginx, or Apache running in the backend. In today's modern .NET core world, you now also have kestrel built into your standard toolset, which is also classed as being "A web server".

Nancy, however, doesn't need ANY of this. It can use it, you can embed Nancy into a classic ASP.NET based web application, or you can even use it with .NET core. However, it's strength is you can add it into ANY .NET based project you have a use case to use it in.

As way of an example, I've used "CefSharp" on several occasions in the past to make the user interface in my desktop Windows applications have an HTML look and feel, and I've used Nancy to build a web-based API directly into the same program so that the one single application served its own HTML content for its own UI directly from its own code base.

In one project, a desktop Windows form based dashboard for help desk operators, I used Nancy to add in some rest endpoints so that the telephone system they were using could push information about the incoming call directly to the operators' screen as the telephone was ringing.

Nancy has a surprising number of use cases that standard tools such as ASP.NET MVC are not equipped to cover, web enabling an application, is not always just about converting an application to run on the web via a user's browser.

Enough Talk, show me some code!

Now you have built a use case for using a fairly old framework 7 years after it was first conceived, it is time to look at the basics of what it can do.

I'm going to be using an up to date version of Visual Studio 2017 for the examples I present in the rest of this article, however you should be able to follow along just as easily in VS2010/2012 & 2015 (I've used all 3 over the years and Nancy has worked just fine with all of them)

For starters, open up Visual Studio, and create a brand new C# console mode application.

Creating a Nancy project on Visual Studio

As you can see, you will create a standard Windows desktop console application, running on the full .NET framework version 4.6.1. As long as your .NET is version 4 or higher, all the current up to date builds of Nancy should work just fine.

You can call your project "NancyStandalone" to start using Nancy’s standalone server mode. Be forewarned, however, that Nancy will try and listen on port 80 by default. So, if you’re not running Visual Studio as an Administrator or if you have a web server such as IIS running on your local Windows machine, the defaults will fail. Soon, you will learn how to change the port, however, first, you need some basic setup.

Once you create your project, right click on references in your solution explorer and go to your "Manage NuGet Packages" option. Once your new get manager opens, click on the browse tab, and enter "Nancy" in the search box. From the results, choose and install Nancy and Nancy.Hosting.Self.

Using the NuGet Packages Manager to install Nancy

Once you’ve added the two packages, close your NuGet manager and head back to your solution explorer. From there, open up your Program.cs file and make sure the code looks like the following:

using Nancy.Hosting.Self;
using System;

namespace NancyStandalone
{
  class Program
  {
    static void Main(string[] args)
    {
      using (var host = new NancyHost(new Uri("http://localhost:80")))
      {
        host.Start();

        Console.WriteLine("NancyFX Stand alone test application.");
        Console.WriteLine("Press enter to exit the application");
        Console.ReadLine();
      }
    }
  }
}

Then, right click on your project in solution explorer and add a new class file. Call this file HelloModule.cs and give it the following code (just don't forget to change namespaces if needed):

using Nancy;

namespace NancyStandalone
{
  public class HelloModule : NancyModule
  {
    public HelloModule()
    {
      Get["/"] = parameters => "Hello World";
    }
  }
}

Now, press F5 to run your application and, if everything works as expected, you should see a console window open with "NancyFX standalone test..." written in it. If you get an error saying "Access Denied", then you will have to change from port 80 to something else.

To do so, you can change the URL in the using statement, in the code you placed in Program.cs. For example, to change to port 8080, change this line:

using (var host = new NancyHost(new Uri("http://localhost:80")))

To this line:

using (var host = new NancyHost(new Uri("http://localhost:8080")))

Then press F5 to re-run your application, go back to your browser, and open http://localhost:8080. If everything goes fine, you will see a page displaying "Hello World":

Hello-Word app developed with Nancy

A URL is equal to a thousand things.

So far, you have got your application to respond only to "root requests". If you try to put anything else in (for example, http://localhost:8080/mypage) you will get an HTTP 404 Not Found response. More specifically, as you have not replaced any of the builtin default error handlers yet, you will actually see a "Tumble Beast" drawn by not other than @theoatmeal "Matthew Inman"(https://twitter.com/theoatmeal) (if you have some time to spare, take a trip to theoatmeal.com you won’t be disappointed).

Default HTTP 404 Not Found response by Nancy

An application that responds to web requests is of no use if it can’t respond to different items and provide different responses based on what it’s been asked.

For this example, change the HelloModule.cs file so that its code now looks as follows:

using Nancy;

namespace NancyStandalone
{
  public class HelloModule : NancyModule
  {
    public HelloModule()
    {
      Get["/"] = parameters => "I am the root";
      Get["/hello"] = parameters => "Hello";
      Get["/world"] = parameters => "World";
    }
  }
}

Then, once again, run your app. You should now be able to still use http://localhost:8080/ (only this time getting a different message) and you should be able to use http://localhost:8080/hello and http://localhost:8080/world respectively.

At the moment though, all you are doing is returning simple strings. You can attach a full-blown function to each URL if you wish. For example, try the following: add a new class called FunctionsModule.cs and add the following code into it:

using Nancy;

namespace NancyStandalone
{
  public class FunctionsModule : NancyModule
  {
    public FunctionsModule()
    {
      Get["/func1"] = parameters =>
      {
        var response = "Hello" + "World";
        return "<h1>" + response + "</h1>";
      };

      Get["/func2"] = parameters =>
      {
        var response = "";
        for(int count = 0; count < 10; count++)
        {
          response = response + count.ToString() + ",";
        }
        response = response.Trim(',');
        return "<p>" + response + "</p>";
      };
    }
  }
}

Yep, it’s a very silly example, but it’s meant to show that you can do some real processing and not just return static strings. If you’re so inclined, you could add in your own HTML generator, and have your Nancy handlers produce full HTML pages containing anything you desire.

You will learn more about the topic of HTML output shortly, for now, you need to concentrate on the functional aspect of what you are doing here.

If you look at the code you have and think about what you’re doing, what you are essentially creating are web callable functions. In just the same way you might include a NuGet package or third-party library, you are creating functionality in program code that can be called from other applications across a network link.

At the moment, you are only listening on localhost, but if you know your local machines IP address and listen on that, then other applications on other systems can also call the functions in your application too. On this machine I’m working on, my application is listening to 192.168.0.13 because I changed the line in my Program.cs file to listen on that instead of localhost.

using (var host = new NancyHost(new Uri("http://192.168.0.13:8080")))

Making Nancy listen to other interfaces

You can even listen on multiple addresses too simply by using an array of URIs rather than just a single URI type:

using Nancy.Hosting.Self;
using System;

namespace NancyStandalone
{
  class Program
  {
    static void Main(string[] args)
    {
      Uri[] uris = new Uri[]
      {
        new Uri("http://localhost:8080"),
        new Uri("http://192.168.0.13:8080")
      };

      using (var host = new NancyHost(uris))
      {
        host.Start();

        Console.WriteLine("NancyFX Stand alone test application.");
        Console.WriteLine("Press enter to exit the application");
        Console.ReadLine();
      }
    }
  }
}

In the code above, you have created an array of URI types and assigned that array to the constructor for NancyHost rather than the single URI. The result is that you can listen on multiple network interfaces. This is useful for example, in situations where you have a server that has to listen on two different interfaces, and respond differently to both. Once you get deep into Nancy’s HTTP handling, you will discover that you can tell which request came from which IP, allowing you to selectively say in program code which connection is allowed access to which functionality.

Putting in the data.

Asking Nancy to return things to you is all well and good. However, any good web framework allows you to send data to it. For example, an ID of a customer record, or the name to include in a functions calculation. Nancy is no exception, you can use and access all the regular HTTP verbs (POST,, DELETE, HEAD, etc.) in the same way as the GET verb you’ve used so far.

You won’t have time to dive deep into the HTTP protocol and its verbs here. But using them is as easy as changing the Get[...] that you’ve been using to the verb you wish to use.

For example, create a new class in your application called PostModule.cs and make sure it has the following code:

using Nancy;
using Nancy.ModelBinding;

namespace NancyStandalone
{
  public class PostModule : NancyModule
  {
    public PostModule()
    {
      Post["/sendname"] = parameters =>
      {
        PostData recievedData = this.Bind<PostData>();

        return new { success = true, message = $"Record recieved name = {recievedData.Name}" };
      };
    }
  }
}

Once you’ve added the post module, add another new class, this time call it PostData.cs, and ensure it looks as follows:

namespace NancyStandalone
{
  public class PostData
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
  }
}

This second class will wrap the data that you will send to the function call.

To test it, you will need a tool that allows you to send raw post requests such as Fiddler, Postman, or one of the many browser extensions available in the many browser stores (you know, the ones designed to grant the average Joe in the street, super-duper hacking powers).

In this article, you will see some Postman screenshots. But the process is the same no matter what you use, set your method to POST, and make the URL equal to http://localhost:8080/sendname. Then, make your body data type application/json and your body payload to look something like:

{
  "id": 1,
  "name": "Peter Shaw",
  "email": "top@secret.com"
}

Once you issue that call, you should see something like the following as the return:

Using Postman to issue an HTTP POST request to Nancy

The C# code in the module is so simple it's frightening. You simply need to make sure that you are using Nancy.ModelBinding, then just use this.Bind<...> to bind to an instance of the data object you expect to receive.

Notice also that in the body data, you specified id, name, and email as all lower case and yet the actual model in C# had capitalized names. Nancy took care of all that for us, using its own built-in JSON serializer. This makes the Camel vs. Pascal vs. no-case just not an issue for the developer using Nancy to add a web endpoint.

As with the GET requests, you just changed the verb to POST, and everything was completely fine.

At this point many of you might be wondering about submitting data on the URL. Well, that’s just as easy too. To see this in action, change your PostModule so that the id in the body data is accepted on the URL, and the name and email are accepted in the data. First, you need to remove the id from your PostData class. So, go ahead and do that first: just simply delete the line.

Then change PostModule.cs so that the line with the Post verb in reads as follows:

Post["/sendname/{id:int}"] = parameters =>

Now, run your app, and open your post sender again. Do the same as before, but this time try just http://localhost:8080/sendname and http://localhost:8080/sendname/1. Note that the first one now fails with an HTTP 404 Not Found error, and the second now succeeds.

You are not going to dive too deeply into using URL parameters. The best place to read about this is the page in the Nancy Wiki. In these examples, you put :int after the parameter to force it to be an int. To try it out, call your post endpoint with something like http://localhost:8080/sendname/one. You will get another 404.

Putting :int after the parameter is just the tip of the iceberg. You can constrict parameters to strings, dates, emails and a whole host of regular expression based goodness. On top of that, different URL patterns are scored in different ways, and the official Wiki is the definitive guide as to how all of that works.

On the subject of parameters, you’re probably wondering how you get a hold of that ID parameter in your C# code. Well, that’s easy too.

Everything passed to Nancy on the URL is passed in a dynamic dictionary. So, it's a simple case of just accessing your "parameters" and the name of the parameter in dot notation.

Remember this line:

Post["/sendname/{id:int}"] = parameters =>

That parameters in the lambda function is what you use to access your URL parameters. You can call it anything you like, but usually it’s either parameters or _ as an indication that it’s not used.

In the case here, the following line of code is all you need to get your id:

int id = parameters.id;

You might be wondering: "what if the parameter is not provided?" Well, as you saw just a moment ago, if it's not provided the call doesn’t succeed. So you would never get far enough for that to cause a problem.

Same applies if the data supplied wasn't an int.

The only time this might be an issue is if you mark the parameter as optional (instructions on the wiki page for that one). In that case, it may be possible to enter this function without supplying that parameter. But don't worry, Nancy has you covered here too. Since it’s a dynamic dictionary, you can simply just check to see if it’s null before using it.

if(parameters.id == null)
{
  // Handle null case here
}

int id = parameters.id;
// Non null case here

Because of the rich binding scenarios available, you will usually find that the most you pass in on the URL is either an integer id, or some kind of GUID. Most of your data will normally be in custom classes that you bind too. Note also too that this same null scenario applies to any objects you bind.

Taking your PostData as an example, if you made your body data so it only included the name property, then name would be bound and email would be left as null. The same rules apply to integers, bools and other types to as long as you make them nullable.

This comes in very handy when you are dealing with objects which may only be partially used. This means that you can design your data classes to hold all the information for a given thing, then use only the fields you need to when you need to.

What about returning data?

Like the inbound dictionary, the return from a route handling function is dynamic. So it's more or less up to you what you return and how, just as long as you return something. If you return a straight up C# class, defined in a similar way to the PostData class you created before, then Nancy will use content negotiation to decide if the return should be JSON, XML, plain text, or some other type.

As mentioned before, now it is time to go back to the subject of returning HTML. You can, if you so choose, construct HTML strings in your handlers and simply just return those. There is an easier way though, view engines.

Nancy at almost every stage of operation can have third-party plugins added either from NuGet or even your own classes that hook into the framework. Plugins can hook everything from authentication and routing, to data binding and beyond, with one of the most active areas being "View Engines".

If you open the NuGet Manager in your test application, and browse for Nancy.ViewEngines, you will see something similar to the following:

NuGet Manager showing Nancy's view engine plugins.

These are not ordered in any specific way. If you scroll down you will find things like "Markdown", "DotLiquid", and "Razor". If you have existing HTML pages and views that use any of these well-known schemes you can use them instantly in your Nancy based handlers without modification.

On top of that, Nancy comes with a view engine prebuilt in that has some elements of the Razor syntax such as Model specification, loops and if/then decisions. In a majority of cases the SSVE (Super-Simple View Engine) that comes as standard will actually suffice for most cases where you would want to template HTML output.

You can even install multiple view engines (something you can’t do with MVC) and have them work alongside each other. Nancy will choose the correct template based on its file extension, which (surprise, surprise) can also be overridden and specified on the return from a handler too. This allows you to have different files, using different view engines for the same view.

"With Nancy, the alternative to the ASP.NET MVC framework, you can support multiple view engines and have them work alongside each other."

Tweet

Tweet This

As way of a final example, you will create a module that uses the built-in view engine to display some HTML from a "PostData" object. So, in your solution explorer, create a new folder called views and create an Empty Text File file called ViewTest.html with the following code in it:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Nancy SSVE Test</title>
</head>
<body>
  <h1>Nancy SSVE Test</h1>
</body>
</html>

Now, create a new class called ViewModule.cs and add the following C# code to it:

using Nancy;

namespace NancyStandalone
{
  public class ViewModule : NancyModule
  {
    public ViewModule()
    {
      Get["/viewtest"] = parameters =>
      {
        PostData data = new PostData()
        {
          Name = "Peter Shaw",
          Email = "top@secret.com"
        };

        return View["ViewTest.html", data];
      };
    }
  }
}

Press F5 to run your app, and try to access http://localhost:8080/viewtest in your browser, you should be "greeted" with the following:

Nancy showing an error page

Yikes, it’s those tumble beasts again, and this time one of them is trying to run off with your router! So, what happened?

Well, like MVC, Nancy will only look in certain places for your view files. In the case of this example, you have built your own stand-alone app. So, Nancy will be looking in the same folder that the exe (Usually bin\debug if you’re running as debug in Visual Studio) is running from.

In order to fix this for your specific case, you need to ensure that the views folder you just created is copied to the same folder as your compiled exe when you build it. You’ve probably seen this done in other apps and technologies too.

So, right click on the ViewTest.html file you created in solution explorer and select properties. There, change the Copy to Output Directory property to "Copy always". This will ensure that every time you build and run your app, that file will be copied to the output folder, in a folder called views.

Change the Copy to Output Directory property of the HTML file

Unfortunately, as you are building your own standalone app, you will have to do this with every single view file that you create. It would be nice if Visual Studio had the same property for folders. Then you could just mark the folder and any files within it. But, as of right now, you will need to do each file separately.

Don’t despair though, like everything else in Nancy, you can provide an override or plugin called a "Path Provider". Then, just like everything else, all you have to do is make it visible, and Nancy will find it.

You can configure a "Path Provider" to search in different places and automatically change search paths as needed. If you were using Nancy’s ASP.NET host rather than the self-host, this is exactly what that would do, and it would allow you just to have your folder as created in the solution explorer searched as is for views.

Writing a "Path Provider" however would require at least another 10 pages or more, just to describe the basics, and this post is already long enough. So, it suffices to say that this is a painful way of doing it. But, as your Nancy journey progresses, you will learn techniques that will solve all these problems and more in very intuitive ways.

If you hit F5 now, you should now be greeted with the following.

Nancny configured with a super-simple view engine

Now, you need to edit the view so that it displays the model you passed to the view in the ViewModule.cs file. To do so, edit the view file so that it looks as follows:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <title>Nancy SSVE Test</title>
</head>

<body>
  <h1>Nancy SSVE Test</h1>
  <p>Hello : <span>@Model.Name</span></p>
  <p>Thank you for submitting your email address as : <span>@Model.Email</span></p>
</body>

</html>

Now, press F5 to run your app again, and you should be greeted with your data being shown:

Nancy configured with an SSVE and showing some data

Aside: Securing Nancy Applications with Auth0

You can easily secure your Nancy applications with Auth0, a global leader in Identity-as-a-Service (IDaaS) that provides thousands of enterprise customers with modern identity solutions. Alongside with the classic username and password authentication process, Auth0 allows you to add features like Social Login, Multifactor Authentication, and much more with just a few clicks.

To secure your Nancy application with Auth0, you will just have to install one more NuGet package (Auth0.NancyFx.SelfHost) and then configure your BootStrapper as follows:

protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
  // ...

  Auth0Authentication.Enable(pipelines, new AuthenticationConfig
  {
    RedirectOnLoginFailed = "login",
    CookieName = "_auth0_userid",
    UserIdentifier = "userid"
  });

  // ...
}

With that in place, you will have to add your Auth0 key in the app.config file:

<appSettings>
    <!-- Auth0 configuration -->
    <add key="auth0:ClientId" value="YOUR_CLIENT_ID" />
    <add key="auth0:ClientSecret" value="YOUR_CLIENT_SECRET" />
    <add key="auth0:Domain" value="YOUR_AUTH0_DOMAIN" />
    <add key="auth0:CallbackUrl" value="https://YOUR_APP/callback" />
</appSettings>

And then block all unauthenticated requests:

public class SecurePage : NancyModule
{
    public SecurePage()
    {
        this.RequiresAuthentication(); //<- This is a new implemetation of default extension
        Get["/securepage"] = o => View["securepage"];
    }
}

Then, to wrap things up, you will have to configure a Callback Handler:

public class Authentication : NancyModule
{
    public Authentication()
    {
        Get["/login"] = o =>
        {
            if (this.SessionIsAuthenticated())
                return Response.AsRedirect("securepage");

            var apiClient = new AuthenticationApiClient(ConfigurationManager.AppSettings["auth0:domain"]);
            var authorizationUri = apiClient.BuildAuthorizationUrl()
                .WithClient(ConfigurationManager.AppSettings["auth0:ClientId"])
                .WithRedirectUrl(ConfigurationManager.AppSettings["auth0:CallbackUrl"])
                .WithResponseType(AuthorizationResponseType.Code)
                .WithScope("openid profile")
                .Build();

            return Response.AsRedirect(authorizationUri.ToString());
        };

        Get["/login-callback"] = o => this
            .AuthenticateThisSession()
            .ThenRedirectTo("securepage");

        Get["/logout"] = o => this
            .RemoveAuthenticationFromThisSession()
            .ThenRedirectTo("index");
    }
}

To learn more about the details on how to use Auth0 with Nancy, please, check the Quick Start documentation here.

Conclusion

SSVE can do a few other simple things too, such as making decisions and loop over data. As with the routing, the base of all knowledge is in Nancy's official Wiki page.

And that’s the basics of using NancyFX in your application, and that’s all they are, the basics. What you have seen in this post is only scratching the surface. You have not even begun to dig into Nancy’s builtin dependency injection container, nor have you delved into power user configuration of the framework by modifying the Bootstrapper class.

Nancy has a staggering amount of functionality, and it’s cross platform too. You can use Nancy quite happily under the Mono Framework for running your apps and services on Linux and Macintosh machines. It is possible also to use it with the newer .NET Core framework, but you have to jump through a few configuration hoops to do so.

One of the core developers on the Nancy team, Jonathan Channon, started a small project originally called Botwin, now renamed to Carter, which allows you to use NancyFX style route definitions in standard ASP.NET and .NET Core MVC. By using Carter, you get all the ease of use of Nancy while maintaining the backing of an enterprise runtime like .Net core.

The community surrounding Nancy is incredible. I (the author) have written several large apps in it and have contributed by writing both a book and helping out answering questions about Nancy on Stack Overflow. It is a framework that even now more than 10 years after it first appeared, that you really should still consider for new projects. And, as the team and community say, we are the keepers of the SDHP (Super Duper Happy Path).

If you want to make your job easy, Nancy is absolutely the route to take for the "Super Duper Happy Path" to creating web applications.

"Nancy, an alternative to the ASP.NET MVC framework, is very powerful and easy to learn."

Tweet

Tweet This

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon