left-icon

NancyFX Succinctly®
by Peter Shaw

Previous
Chapter

of
A
A
A

CHAPTER 6

Routing

Routing


Routing is where most of the magic happens in Nancy, and defining routing modules are the meat and bones that typically make up a Nancy application. Defining routes in a Nancy application is quite a bit different than defining them in something like an ASP.NET MVC application.

Taking ASP.NET MVC as an example, you typically create a class known as a controller. This class provides routing by convention in most cases. By defining your controller class name and the names of the methods within that class, you define the "route path" the code in the class will respond to.

Take the following example:

Code Listing 3

using System;

using System.Linq;

using System.Web.Mvc;

namespace Intranet.WebUi.Controllers

{

  public class HomeController : Controller

  {

    public ActionResult Index()

    {

      return View();

    }

  }

}

This snippet of code from a standard ASP.NET MVC application defines a result called index in a route called home, meaning that to route a request to this code, you would likely use

/home/index

as the request path. With Nancy, things are a little different.

First off, all routes defined in a Nancy application must inherit from the base class NancyModule. Secondly, rather than have a separate class for each route (as in the MVC example), you define your routes in the constructor of your module class, using restful verbs to define the route type.

Unfortunately, this can lead to classes built completely of large constructors, but there are various ways and means to get around that.

Before we go any further however, we need to cover the concept of…

Restful verbs

If you're used to seeing web requests made only from a web browser, you might not realize that underneath it all is quite a complex protocol. If you're doing any work at all in web development, then you've most likely heard of this referred to as the HTTP Protocol. HTTP works by using a set of verbs that represent certain actions the client wishes the server to take on its behalf.

There's a full W3C specification on how all the verbs are intended to work and what they represent, but I don't recommend reading it if you’re new to the subject. Instead, I recommend HTTP Succinctly from the Syncfusion Succinctly series library.

In the context of using this with Nancy, however, we don't need to go into a lot of detail. In fact, in order to build an application that is considered REST-compliant, only the following verbs need to be used:

  • GET
  • POST
  • PUT
  • DELETE

GET, as the name suggests, is used to retrieve data from the service; likewise, DELETE is used to request data be deleted. PUT and POST, however, often cause confusion.

Following the spec, PUT is taken to mean "put an entire replacement for a resource in place," whereas POST is taken to mean "post an addition to a given resource." Many developers creating restful-based applications, however, don't use anything more than GET and POST for much of what they need to do.

Nancy takes this one step further, and unlike most REST/web-based frameworks, lets you define your own verbs, in a sense allowing you to create your own domain-specific language that operates over HTTP.

Let’s dissect an example Nancy route module and see what makes it tick.

Our first Nancy route module

Take a look at the following piece of code:

Code Listing 4

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes()

    {

      Get[@"/"] = _ => Response.AsFile("index.html", "text/html");

    }

  }

}

When it comes to Nancy, it really doesn't get much simpler than this.

That entire eleven-line class, when added to a Nancy project, will serve the standard plain HTML page called "index.html" and located in a folder called "Content" to anyone that makes a request to the "/" root path of the application it's contained in.

Content is the default place that Nancy will look to find any files you return using this method. We'll see a little more about this in the next chapter on views, but for now, just ensure that your project has a folder called Content, and that you place your HTML files for this chapter in there.

Putting aside the usual namespace and class parts of the code, the two main points that make this a Nancy route module are the addition of : NancyModule after the class definition, and the Get rule placed in the constructor.

The Get rule means that this will respond to HTTP calls made using the GET verb, as mentioned in the previous section, and the code following it would be executed in response to the path for that GET request being the root path.

If you wanted to expand the module to handle GET, POST, PUT, and DELETE, you would do something similar to the following:

Code Listing 5

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes()

    {

      Get[@"/"] = _ => Response.AsFile("index.html", "text/html");

      Put[@"/"] = _ => Response.AsFile("index.html", "text/html");

      Post[@"/"] = _ => Response.AsFile("index.html", "text/html");

      Delete[@"/"] = _ => Response.AsFile("index.html", "text/html");

    }

  }

}

The path or route that the module needs to listen for is enclosed in the square parenthesis of the call to the verb being used. So, for example, you could modify the module to something like:

Code Listing 6

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes()

    {

      Get[@"/allpeople"] = _ => Response.AsFile("index.html", "text/html");

      Put[@"/allpeople"] = _ => Response.AsFile("index.html", "text/html");

      Post[@"/newperson"] = _ => Response.AsFile("index.html", "text/html");

      Delete[@"/singleperson"] = _ => Response.AsFile("index.html", "text/html");

    }

  }

}

For the purposes of demonstrating the code, everything (at least for now) responds with a static page. In reality, you would probably want to respond to each with appropriate code and responses to perform the actual functions requested. You will see more on the subject of responses in a later chapter.

You can also specify a common path for your module. Imagine, for example, we had the following:

Code Listing 7

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes()

    {

      Get[@"/single"] = _ => Response.AsFile("index.html", "text/html");

      Put[@"/single"] = _ => Response.AsFile("index.html", "text/html");

      Post[@"/new"] = _ => Response.AsFile("index.html", "text/html");

      Delete[@"/single"] = _ => Response.AsFile("index.html", "text/html");

    }

  }

}

You can quickly see that things become difficult to read. What's more, if you are trying to create a consistent approach to your API, you might want to reuse the single and new URL terminology for other resources your application may have to deal with.

Once again, Nancy makes this an easy problem to solve by calling the base constructor of the NancyModule parent class with the root path from which everything in your route module should continue.

Code Listing 8

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes() : base("/people")

    {

      Get[@"/single"] = _ => Response.AsFile("index.html", "text/html");

      Put[@"/single"] = _ => Response.AsFile("index.html", "text/html");

      Post[@"/new"] = _ => Response.AsFile("index.html", "text/html");

      Delete[@"/single"] = _ => Response.AsFile("index.html", "text/html");

    }

  }

}

By adding this base call, in one move you've changed all the URLs your module will respond to from:

  • GET /single
  • PUT /single
  • POST /new
  • DELETE /single

to:

  • GET /people/single
  • PUT /people/single
  • POST /people/new
  • DELETE /people/single

With a bit of creative thinking and some clever software construction, you could even reuse one controller in many different places.

Before we move on, a word of warning on "restful verbs" is needed. Some hosting platforms (including ASP.NET/IIS) do not enable some of the more uncommon verbs by default.

In the previous examples, if you’re running on IIS, you'll find that PUT and DELETE will actually result in the server returning the following error: 503 Method not allowed.

This is not a fault of Nancy, but is due to the fact that IIS only allows the two most common verbs (GET and POST by default, on the grounds of security. It's not difficult to fix it, however; take a look at this Stack Overflow post to find out how.

Most of the different resolutions can be found this post, and are easy enough to understand. You may also find that some methods cannot be called against the regular HTML file response we’re using; this will be resolved using views, as we'll see in the next chapter.

Route parameters

Just as with other web toolkits, you can set your Nancy routing modules up to receive various items of data. This data can range from simple URL parameters such as a record ID or blog category on a GET request, right through to a complex object on a POST or PUT request.

In this chapter, we'll look at simple route-based data, and leave complex data objects for a later chapter.

You've already seen how easy it is to change a URL in a Nancy module to respond to a given request, but what if that request needs to pass in something like a record ID? In our previous examples, you saw a routing example that might be used with a single type of database record, the type that you might use to manage a single database record using a REST interface in your application. Getting access to something like an ID for this purpose is incredibly easy.

Let's take another look at our previous example, but this time just with the one get request available:

Code Listing 9

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes() : base("/people")

    {

      Get[@"/single"] = _ => Response.AsFile("Pages/index.html", "text/html");

    }

  }

}

If you know C#, you might recognize that following our route definition, we’re actually using a Lambda to provide the code we wish to run when Nancy calls that route. If you don't know what a Lambda is, don't worry; you don't need to know what they are in order to make use of them here.

The important thing to recognize is the underscore symbol placed between the two equal symbols.

While not a requirement, it's become somewhat of a self-imposed standard by many users of Nancy to use the underscore to mean "I don't care about" or "I'm not using" any parameters for this route. However, if you were to set the route up to accept a parameter, you could still use the underscore to access it. Many people like to use something like the word “parameters,” as the following shows:

Code Listing 10

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes() : base("/people")

    {

      Get[@"/single"] = parameters => Response.AsFile("Pages/index.html", "text/html");

    }

  }

}

When you do this, you can then access any parameters specified on the URL by using the name parameters and the name of the parameter you want to access.

To actually specify the parameters you wish to use, use the { } syntax (very similar to ASP.NET MVC) as follows:

Code Listing 11

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes() : base("/people")

    {

      Get[@"/{id}"] = parameters =>

      {

        var myRecordId = parameters.id;

        return Response.AsFile("Pages/index.html", "text/html");

      };

    }

  }

}

You can also force Nancy to restrict only the parameters you supply to certain types. For example:

Code Listing 12

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes() : base("/people")

    {

      Get[@"/{id:int}"] = parameters =>

      {

        var myRecordId = parameters.id;

        return Response.AsFile("Pages/index.html", "text/html");

      };

    }

  }

}

Notice the addition of int on the route parameter; any attempt to request that route with anything other than an integer-based number will result in a 404 not found error.

Nancy defines many of these constraints, mostly tied to the .NET primitive types such as:

  • long
  • decimal
  • guid
  • bool
  • datetime

There are others too, but I'll leave it as an exercise for you to take a look at the NancyFX Wiki (currently located on GitHub); it has a full list of every routing constraint available.

You can provide as many parameters as you wish in the URL—your imagination is the limit (and how complicated you want your URLs to be).

Code Listing 13

using Nancy;

namespace nancybook.modules

{

  public class BaseRoutes : NancyModule

  {

    public BaseRoutes() : base("/people")

    {

      Get[@"/person/{age:int}/{surname}/categories/{category}/{city}"] = parameters =>

      {

        return Response.AsFile("Pages/index.html", "text/html");

      };

    }

  }

}

The route for this example might be long and unwieldy, but it's valid and will work without problem. A URL such as /people/person/21/shaw/categories/developer/durham would match the route definition without an issue, and would make the values 21, shaw, developer and durham available in the parameters collection as properties named age, surname, category and city.

Summary

In this chapter, you learned how to make Nancy respond to routes set up in your application, and you learned that these routes are no different than any other URL-based routes that frameworks such as ASP.NET MVC might use.

You saw how Nancy defines them, and how you have full control of the route layout rather than the name of the class in which it's defined, and that you have full control over the verb used and the parameters and layout of the URL to match it.

In the next chapter, we'll take a look at view engines and see how to return something more than just plain HTML documents.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.