Building a RESTful API with NancyFx
Contents
- Introduction
- Prerequisites
- The basics of NancyFx
- Model binding and validation
- Bootstrapping NancyFx
- Hosting NancyFx
- Authentication and security
- Putting it all together
Introduction
In the following, I will show you how easy is to build a simple RESTful API with NancyFx. As it is said by its creators "Nancy is a lightweight, low-ceremony, framework for building HTTP based services on .NET and Mono. The goal of the framework is to stay out of the way as much as possible and provide a super-duper-happy-path to all interactions." (the full documentation can be found at https://github.com/NancyFx/Nancy/wiki/Documentation). To understand and apply this, basic knowledge of C# programming language and RESTful API concepts is required.
Prerequisites
- IDE: Microsoft Visual Studio or Visual Studio Code
- NuGet packages:
- https://www.nuget.org/packages/FluentValidation/3.4.0
- https://www.nuget.org/packages/Microsoft.Owin/3.1.0
- https://www.nuget.org/packages/Microsoft.Owin.Host.HttpListener/3.1.0
- https://www.nuget.org/packages/Microsoft.Owin.Hosting/3.1.0
- https://www.nuget.org/packages/Nancy/1.4.3
- https://www.nuget.org/packages/Nancy.Authentication.Basic/1.4.1
- https://www.nuget.org/packages/Nancy.Owin/1.4.1
- https://www.nuget.org/packages/Nancy.Serialization.JsonNet/1.4.1
- https://www.nuget.org/packages/Nancy.Validation.FluentValidation/1.4.1
- https://www.nuget.org/packages/Newtonsoft.Json/10.0.2
- https://www.nuget.org/packages/Owin/1.0.0
The basics of NancyFx
I am using a simple POCO as a resource:
public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
To create get/add/modify/delete routes for the above resource all you have to do is to create a public class that inherits Nancy.NancyModule (NancyFx autoregisters this classes). In order to define a route in NancyFx, routes are defined in the constructor, you need to specify a Method + Pattern + Action + (optional) Condition:
public class PersonModule : NancyModule { public PersonModule() : base("rest/person") { ... Get[""] = GetAllAction; Get["{id:int}"] = GetAction; Post[""] = AddAction; Put["{id:int}"] = ModifyAction; Delete["{id:int}"] = DeleteAction; } private dynamic GetAllAction(dynamic parameters) { return PersonRepository.Instance.GetAll(); } ... }
NancyFx supports the following methods DELETE, GET, HEAD, OPTIONS, POST, PUT and PATCH.
Model binding and validation
The "model binding" capabilities of NancyFx makes it easier to handle scattered context information (query string, XML or JSON request body, etc.) by transforming it in a instance of a concrete type:
public class PersonModule : NancyModule { ... private dynamic ModifyAction(dynamic parameters) { var model = this.BindAndValidate<Person>(); if (!ModelValidationResult.IsValid) throw new ApplicationException("Object is not a valid person"); PersonRepository.Instance.Modify(model); return HttpStatusCode.OK; } ... }
Another important aspect for which NancyFX offers support is the "model validation". To create a validator for a concrete type all you have to do is to create a public class that inherits FluentValidation.AbstractValidator<T> (NancyFx autoregisters this classes):
public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { RuleFor(p => p.FirstName).Must((p, firstName) => !string.IsNullOrWhiteSpace(firstName)) .WithMessage("First name must be filled in"); RuleFor(p => p.LastName).Must((p, lastName) => !string.IsNullOrWhiteSpace(lastName)) .WithMessage("Last name must be filled in"); } }
Bootstrapping NancyFx
NacyFx is based on convention over configuration, meaning that is up and running with little configuration effort. If you want to tweak defaults and conventions all you have to do is to create a public class that inherits Nancy.DefaultNancyBootstrapper (NancyFx autoregisters this class), for example intercepting the request in key moments (before, after, on error):
public class Bootstrapper : DefaultNancyBootstrapper { ... protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); pipelines.OnError += (ctx, e) => { return new Response().WithStatusCode(HttpStatusCode.InternalServerError); }; pipelines.BeforeRequest += ctx => { ... return null; }; pipelines.AfterRequest += ctx => { ... }; } ... }
Hosting NancyFx
NancyFx can be hosted in a lot of containers: ASP.NET, WCF, Azure, etc. I will show you how to run as a standalone HTTP service using OWIN (on a specified port):
class Program { public class Startup { public void Configuration(IAppBuilder app) { app.UseNancy(); } } static void Main(string[] args) { var url = "http://+:8080"; using (WebApp.Start<Startup>(url)) { Console.WriteLine("Running on {0}", url); Console.WriteLine("Press enter to exit"); Console.ReadLine(); } } }
Authentication and security
NacyFx doesn't have built-in authentification support, but there are some out-of-the-box authentication mechanisms:
- Forms (Nancy.Authentication.Forms)
- Basic (Nancy.Authentication.Basic)
- Stateless (Nancy.Authentication.Stateless)
In the case of basic authentification you have to implement Nancy.Security.IUserIdentity and Nancy.Authentication.Basic.IUserValidator and to enable it trough the bootstrapper.
NancyFx allows you to enforce security at a module or at a route level:
public class PersonModule : NancyModule { public PersonModule() : base("rest/person") { this.RequiresAuthentication(); this.RequiresClaims(UserIdentity.HANDLEPERSON_PERMISSION); ... } ... }
Putting it all together
The source code can be found on GitHub at:
Comments
Post a Comment