Simplifying .Net REST API development: Nancy, self-hosting and ASP.Net Core

From my archive - originally published on 12 July 2016

REST API development using ASP.Net WebAPI can seem so fussy compared to other ecosystems. There’s a big application server to deal with, dense XML-based configuration and a lot of code to write just to stand up a basic API.

Compare this with simpler, DSL-style approaches such as Sinatra on Ruby. This lets you push out a self-hosted API with sensible defaults with a minimal amount of ceremony, i.e.

require 'sinatra'
 
get '/' do
  "Hello, World!"
end

Most ecosystems seem to offer “Hello world” examples that are pretty low friction compared to WebAPI. For example, Go just expects you to set up a server and configure a few routes while Node.js has its own Sinatra-inspired libraries such as Express.

Why does this matter? When you’re developing a RESTful API in the full, HATEOAS-enabled sense then you need to focus on the design of the resources and methods that are being exposed. This requires a degree immediate clarity that can be easily obscured by boilerplate code.

A more minimal approach to developing and deploying APIs also be useful in building finely-grained microservices. When you are pushing out numerous of small, self-contained services you don’t want the feature logic to be swamped by the demands of boilerplate, configuration and hosting.

Nancy and “low ceremony” API development

NancyFX gained traction in the .Net space by bringing a similar lightweight, “low ceremony” framework to the .Net space. The syntax is focused around routes and responses and it provides sensible defaults that do not require any configuration files. The build-in dependency resolution allows you to change any aspect of request pipeline by implementing interfaces.

Note that “low ceremony” needs qualification as there’s always some boilerplate involved in API development. Four-line “hello world” samples aside, “low ceremony” generally means you have to accept somebody else’s idea of sensible defaults or start writing code. For example, the default error response in Nancy is an HTML page containing a grim looking embedded image and a jokey error message (“Oh noes!”). This is easy enough to override with an IStatusCodeHandler instance, but it is essential scaffolding you have to write to avoid leaking implementation detail.

ASP.Net WebAPI has improved considerably since Nancy was released. For example, features such as IHttpActionResult and HttpRequestContext have improved the testability of controllers, while attribute routing has made it easier to wire up routes. Despite this, it continues to provides a weighty and opinionated framework in comparison to the lighter-touch, declarative approach of Nancy.

Do we still need IIS?

One of the nicer aspects of Nancy was that it was host agnostic. It ran on Mono. It could be deployed pretty much anywhere: via IIS, WCF, embedded in an executable or running as a Windows service. Best of all, it was not dependent on System.Web and implemented its own pipeline that could be customised through hooks and plugins.

The release of OWIN freed things up for ASP.Net by providing a standard for decoupling servers and applications. Despite this growing flexibility, many developers in the .Net space are so accustomed to using IIS that they cannot conceive a solution without it.

It is a mistake to regard solutions involving IIS as somehow more “enterprise-ready”. IIS exposes a complex processing pipeline containing many features that a REST API does not need. The static content handling and compression in IIS may be impressive, but irrelevant. Process recovery can be taken care of by running the executable as a service with a service manager like NSSM. Other features such as monitoring and logging should arguably be application-level concerns.

This is not to say you should be exposing self-hosted applications directly to the internet. You’ll need some form of reverse proxy to expose a number of separate applications to the externally-opened HTTP ports (i.e. 80 and 443). Other concerns such as request validation and authentication also need to be taken care of somewhere.

An API Gateway is often used to address many of these concerns in API ecosystems. This acts as a “gatekeeper” that separates public-facing end-points from the applications that execute requests. It is first and foremost a security measure that can take care of authentication and request validation, but it can also serve as a reverse proxy to expose numerous self-hosted end-points through the same public-facing port.

Onwards with ASP.Net Core

ASP.Net Core finally breaks the IIS dependency. An ASP.Net Core application runs as a stand-alone console application and uses a self-hosted web server (Kestrel) to process requests. This allows it to offer a container-friendly deployment pipeline that does not rely on any frameworks or servers.

The example below is the minimal code required to stand up a simple API end-point:

namespace CoreExample
{
    // Bootstraps the application
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseStartup<Startup>()
                .Build();
 
            host.Run();
        }
    }
 
    // Used to inject in the MVC framework
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }
 
        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }
 
    // Example controller
    public class TestController : Controller
    {
        [HttpGet("api/helloworld")]
        public object HelloWorld()
        {
            return new { message = "Hello World" };
        }
    }
}

You can still host Core applications in IIS, but they will run out of process using an HTTP module called AspNetCoreModule. This is responsible for loading the core application and ensuring that it stays loaded by detecting any crashes. This module bypasses much of the normal IIS request cycle and redirects traffic to the underlying core application.

This style of application is similar to environments such as Ruby where you can bootstrap self-hosted applications using a single command line statement. They can be mounted behind an API gateway, web server or other proxy technology of your choice.

Many of the remaining advantages that made Nancy look so attractive several years ago have been cancelled out by .Net Core. Dependency injection is now included out of the box. It is also host-agnostic and you can stand up a new end-point without any configuration files.

The one thing Nancy still provides is options. Nancy represents a different approach that brings in great ideas from other ecosystems. For example, where ASP.Net WebAPI binds you into a model\controller pattern you have a lot more freedom over how to organise your Nancy code. This flexibility is something developers are accustomed to outside of the .Net ecosystem.