In the IoT world of embedded devices, small is beautiful and small doesn’t come much better than the NancyFX Web framework.
Nancy is a small, exceptionally easy to use REST and service framework.
You can use it in place of ASP.NET MVC; you can use it on its own in a self-hosting environment, just as you might do if you’re deploying applications using NodeJS for example.
Nancy’s strength, however, comes from far more than just its tiny size. Into the default install, it manages to pack a full IoC container, a built-in testing framework, the ability to build some very complex routing rules, and a modular architecture that makes it amazingly easy to plug in extra features with little or no effort.
Perhaps one of the best things about Nancy, however, especially when it comes to the “Internet Of Things,” is that it runs with no modification under MONO. In fact, Nancy is so successful in this respect that I’ve built and tested Nancy-based applications fully on a Windows system, and then just copied the EXE and resultant DLLs to a target Linux server running MONO and had everything run immediately and with 100% success.
Nancy runs perfectly on small embedded boards such as the Arduino and RaspberryPi, meaning if you’re in a constrained environment where you can’t install a full-blown Web server such as Apache but you need more configuration possibilities than NginX+PHP gives you, NancyFX is a perfect choice.
Let’s Build a Server….
As is the norm for this column, launch Visual Studio and start a console mode project.
Head straight to NuGet and install Nancy and Nancy.Hosting.Self:
Figure 1: Nancy NuGet packages
Once these two packages are added, make sure your Program.cs file looks as follows:
using System; using Nancy.Hosting.Self; namespace NancyMicroservice { class Program { static void Main() { var url = "http://127.0.0.1:9000"; using(var host = new NancyHost(new Uri(url))) { host.Start(); Console.WriteLine("Nancy Server listening on {0}", url); Console.ReadLine(); } } } }
If you press F5 and run your application, Nancy now will start up, and start listening on the address provided in the URL.
Figure 2: Our server runs.
For now, however, you have no routes, so everything you try to browse to will give you a 404 courtesy of The Oatmeal.
Figure 3: However, we have no routes, so all we get is a 404.
It’s at this point that you get to see some Nancy magic in play.
What’s in a Module?
If your server is still running, press Return to get it to quit, and then switch back to VS.
Unlike most frameworks you might be familiar with, Nancy uses what it calls “Modules” to provide its routing.
A module is a class that’s derived from “NancyModule” and is automatically found and used without you doing any more than writing a simple class.
Add a new class to your project and call it “RootRoutes.cs”. Then, make sure it has the following code in:
using Nancy; namespace NancyMicroservice { public class RootRoutes : NancyModule { public RootRoutes() { Get["/"] = parameters => { return "Hello World"; }; } } }
If you press F5 now, and point your browser at http://127.0.0.1:9000, you should see “Hello World” appear.
Figure 4: “Hello World”, Nancy Style
One of Nancy’s biggest strengths is its ability to return and manipulate JSON with ease. Add another route to the class you just created, so that your file now looks as follows:
using Nancy; namespace NancyMicroservice { public class RootRoutes : NancyModule { public RootRoutes() { Get["/"] = parameters => { return "Hello World"; }; Get["jsontest"] = parameters => { var test = new {Name = "Peter Shaw", Twitter = "shawty_ds", Occupation = "Software Developer"}; return Response.AsJson(test); }; } } }
If you run this now, and browse to http://127.0.0.1:9000/jsontest, you should see something like the following. (Don’t worry if yours is not identical; I have a Chrome plugin called ‘JsonView’ installed. As long as you get JSON returned, all is okay.)
Figure 5: Nancy does JSON
Nancy also can do XML. In fact, Nancy has a full “Content Negotiation” algorithm built into it, and if you change your route handler so that it simply just returns the “test” object in the previous code, Nancy will actually try to figure out what the best format to return is.
If you try this in the browser, for example, with just the “test” object and no ‘AsJson’ call, you’ll actually get a 500 server error.
Why?
Well, because the browser will request HTML, and you currently don’t have a HTML view set up to format and return your “test” object back to the browser. If, however, you test the URL using a dedicated tool such as Fiddler or Postman, you’ll quickly see that changing the request MIME Type between “application/json” and “application/xml” will cause Nancy to automatically send you the correct type.
Nancy also comes with a built-in View Engine called SSVE (Super Simple View Engine), and if you create a “Views” folder in your application, and then make sure that folder is always copied to your output folder, Nancy will automatically search in that folder for any HTML files that have the same name as your object.
In our sample above, however, we’re using an anonymous object, so creating a view won’t work because our object has no type name.
Going back to our routes, there’s one thing that always gets me about Nancy and that’s how all your route code is always in the constructor. I personally like to be able to use the code folding built in to Visual Studio, and that doesn’t always work reliably when you have a large constructor defining a lot of routes.
For this reason, I usually like to create my “Modules” in the following way:
using Nancy; namespace NancyMicroservice { public class RootRoutes : NancyModule { public RootRoutes() { Get["/"] = Index; Get["jsontest"] = JsonTest; } private dynamic Index(dynamic parameters) { return "Hello World"; } private dynamic JsonTest(dynamic parameters) { var test = new { Name = "Peter Shaw", Twitter = "shawty_ds", Occupation = "Software Developer" }; return Response.AsJson(test); } } }
For me, this makes my code much neater and allows me to hide methods I’m not working on, this is of course just personal preference and however you manage it, is entirely up to you.
Routing Parameters
By now, you’ve probably spotted the “parameters” entry in both pieces of code.
Nancy again magically wires all of this up. You don’t need to pre-define template routes as you do in ASP.NET MVC.
You can simply just define your route, directly in your constructor then use the “parameters” (or whatever you name it) variable to access those route segments.
Add another route to your class, so that it now looks as follows:
using System; using Nancy; namespace NancyMicroservice { public class RootRoutes : NancyModule { public RootRoutes() { Get["/"] = Index; Get["jsontest"] = JsonTest; Get["hello/{name}"] = HelloName; } private dynamic HelloName(dynamic parameters) { var name = parameters.name; return String.Format("Hello there {0}", name); } private dynamic Index(dynamic parameters) { return "Hello World"; } private dynamic JsonTest(dynamic parameters) { var test = new { Name = "Peter Shaw", Twitter = "shawty_ds", Occupation = "Software Developer" }; return Response.AsJson(test); } } }
If you run your server now, and point your browser at http://127.0.0.1:9000/hello/shawty, you should see:
Figure 6: Nancy with route parameters
Anything enclosed in {} in a URL route will be available this way without any extra work.
But what about posting data, I hear you all say?
Well, for this, Nancy has automatic binding available, and as long as it can find a parameter or parameters that match the fields in your object, it will automatically populate the object for you.
Nancy will look on the URL, in the request headers, and in the payload body to find matches. It will even decode the body correctly in most cases, irrespective of you sending JSON or key value pairs, and most of the time it will get this right, again without you having to do any kind of extra configuration.
Add a new class to your project, called “Person.cs”, as follows:
namespace NancyMicroservice { public class Person { public string Name { get; set; } public string Twitter { get; set; } public string Occupation { get; set; } } }
Then, add a new route handler to your “RootRoutes.cs” file, so that it now looks as follows:
using System; using Nancy; using Nancy.ModelBinding; namespace NancyMicroservice { public class RootRoutes : NancyModule { public RootRoutes() { Get["/"] = Index; Get["jsontest"] = JsonTest; Get["hello/{name}"] = HelloName; Post["posttest"] = PostTest; } private dynamic PostTest(dynamic parameters) { var myPerson = this.Bind<Person>(); return String.Format("Hello there {0} with twitter handle {1} who works as a {2}", myPerson.Name, myPerson.Twitter, myPerson.Occupation); } private dynamic HelloName(dynamic parameters) { var name = parameters.name; return String.Format("Hello there {0}", name); } private dynamic Index(dynamic parameters) { return "Hello World"; } private dynamic JsonTest(dynamic parameters) { var test = new { Name = "Peter Shaw", Twitter = "shawty_ds", Occupation = "Software Developer" }; return Response.AsJson(test); } } }
If you then post some data to this route and name your three parameters as ‘Name’, ‘Twitter’, and ‘Occupation’, Nancy will pick them up and use them:
Figure 7: Testing the post acceptor using Fiddler
You’ll see, when you look at the response, that Nancy returns the data it found in the post request:
Figure 8: The response returned by our Post-test in Fiddler
There’s a whole lot more to learn than just what I’ve presented here in this post, but if you need to build a simple REST-based server interface for your IoT devices, NancyFX fits the bill perfectly.
I’ve deployed solutions to RaspberyPi (for monitoring weather stations) and Arduino-based devices, and I’ve even used it for full-scale Web applications with authentication and regular security practices. Nancy scales, no matter what you deploy it on. I’ve recently written a book on NancyFX that will be available in the not too distant future in the Succinctly free eBook range from Syncfusion. If you want to go deeper and really find out what Nancy can do, keep your eye open for it hitting the interwebs.
.NET giving you .NOT? Can’t find a routine or API to finish that last little requirement in your project? Drop me a comment below and I’ll see what I can do to help.