Building Tweets from the Vault: NancyFX tips and tricks

Andrew Harcourt Mon 19 Jan 2015

In Building Tweets from the Vault: Azure, TeamCity and Octopus, I wrote about the hosting and infrastructure choices I made for Tweets from the Vault. This article will cover a bit more about the framework choices, notably NancyFX.

NancyFX

NancyFX may sound like a bit more of an esoteric choice, especially to the Microsoft-or-die crowd. I've been having a pretty fair amount of success with Nancy, however. I love the way that it just gets out of the road and provides a close-to-the-metal experience for the common case but makes it simple to extend behaviour.

By all means, it's not perfect - I'm not a huge fan of "dynamic, dynamic everywhere" - but it's way better than MVC for my needs. The upgrade path is a whole lot less troublesome, too - the best advice I've found for upgrading between major versions of MVC is to create a new project and copy the old content across.

Application structure

The equivalent of an MVC controller in NancyFX is the module. In a typical MVC controller, there are lots (usually far too many) methods (controller actions) that do different things. While this isn't strictly a feature of the framework, all the sample code tends to guide people down the path of having lots of methods on an average controller, with a correspondingly large number of dependencies.

In MVC, routing to controller actions is taken care of my convention, defaulting to the controller's type name and method name. For instance, the /Home/About path would (by default) map to the About() method on the HomeController class.

Nancy routes are wired up a little bit differently. Each module gets to register the routes that it can handle in its constructors, so if I were to want to emulate the above behaviour I'd do something like this:

public class HomeModule : NancyModule { public HomeModule() { Get["/Home/Index"] = args => /* some implementation here /; Get["/Home/About"] = args => / some implementation here /; Get["/Home/Contact"] = args => / some implementation here */; } }

Obviously, if we want the same Nancy module to handle more than one route then we just wire up additional routes in the module's constructor and we're good.

This is nice in a way but it's also a very easy way to cut yourself and I tend to not be a fan. Not only that, but it still leads us down the path of violating the Single Responsibility Principle in our module.

My preference is to have one action per module and to name and namespace each module according to its route. Thus my application's filesystem structure would look something like this:

app
    Home
        Index.cs
        About.cs
        Contact.cs

This makes it incredibly easy to navigate around the application and I never have to wonder about which controller/module/HTTP handler is serving a request for a particular path.

My About.cs file would therefore look something like this (for now):

public class About : NancyModule
{
    public About()
    {
        Get["/Home/About"] = args => /* some implementation here */;
    }
}

RoutedModule

One problem with the above approach is that it's not refactoring-friendly. If I were to change the name of the About class then I'd also need to edit the route registration's magic string. Magic strings are bad, mmmkay?

A simple approach for the common case (remembering that it's still easy to manually register additional routes) is to just derive the name of the route from the name and namespace of the module. (Hey, I didn't say that all of MVC was bad.)

public abstract class RoutedModule : NancyModule
{
    protected RoutedModule()
    {
        var route = Route.For(GetType());
        Get[route, true] = (args, ct) => HandleGet(args, ct);
        Post[route, true] = (args, ct) => HandlePost(args, ct);
    }

    protected virtual async Task<dynamic> HandleGet(dynamic args, CancellationToken ct)
    {
        return (dynamic) View[ViewName];
    }

    protected virtual Task<dynamic> HandlePost(dynamic args, CancellationToken ct)
    {
        throw new NotSupportedException();
    }

    protected virtual string ViewName
    {
        get { return this.ViewName(); }
    }
}

This now allows for our About.cs file to look like this:

public class About : RoutedModule
{
}

Routes

We're not quite there yet. I'm not a fan of magic strings and in the above example you can see a call to a static Route.For method. That method is where the useful behaviour is, and it looks like this:

public static class Route
{
    private static readonly string _baseNamespace = typeof (Index).Namespace;

    public static string For<TModule>() where TModule : RoutedModule
    {
        return For(typeof (TModule));
    }

    public static string For(Type moduleType)
    {
        var route = moduleType.FullName
                              .Replace(_baseNamespace, string.Empty)
                              .Replace(".", "/")
                              .Replace("//", "/")
                              .ToLowerInvariant();
        return route;
    }

    public static string ViewName(this RoutedModule module)
    {
        // Left as an exercise for the reader :)
    }
}

This allows us to have a completely refactor-friendly route to an individual action. There are a couple of similar routing efforts for MVC, notably in MVC.Contrib and MvcNavigationHelpers, but this lightweight approach doesn't require building and parsing of expression trees. (It's worth noting that it doesn't account for a full route value dictionary, either, but you can add that if you like.)

In our views, our URLs can now be generated like this:

<a class="navbar-brand" href="@(Route.For<Index>())">
    Tweets from the Vault
</a>

and in our modules, like this:

return new RedirectResponse(Route.For<Index>());

A quick ^R^R (Refactor, Rename, for all you ReSharper Luddites) of any of our modules and you can see that we haven't broken any of our links or redirects.

In the next post in this series, we'll take a quick look at authenticating with Twitter using OAuth.

Building Tweets from the Vault: yet another Bootrap site?

Andrew Harcourt Sat 17 Jan 2015

There's even a Tumblr for this.

The reality, however, is that Bootstrap is incredibly popular for good reason. It's responsive out-of-the-box, is delivered via other people's CDNs (for which I thank you :) ) and provides a relatively familiar UI paradigm.

In keeping with the "minimum viable product" theme, Bootstrap allows for very quick... err... bootstrapping of a pleasant, clean, simple web application with the minimum of fuss.

This is just a quick post to get the "Yet another Bootstrap site?" question out of the way. In the next post in this series I'll look in a bit more detail at NancyFX and some sneaky tricks to make it refactoring-friendly.

The boring traveller

Andrew Harcourt Mon 12 Jan 2015

So... it would appear that I've landed in BKK and am in a hotel somewhere.

This one's a short trip (work, not play) and I'm going to be the most boring traveller known to humankind.

Thailand is renowned for its:

  • culture
  • food
  • beaches
  • night life

and while I'm here I'm going to:

  • arrive when it's dark, fly out when it's dark and work in between
  • not try any too-adventurous dishes as my flight back is pretty soon after I arrive and if I'm sick for a nine-hour flight it's not going to be pleasant. And I really need to be functional when I return.
  • not have any daylight hours to go exploring
  • sleep when it's dark.

What I am going to do is take every opportunity to fill up my phone with photos. Watch this space.

About me

My name is Andrew Harcourt.

I'm a software engineer and project rescue specialist. I'm a Principal Consultant at Readify. I also run my own photography company, Ivory Digital.

Work

I'm a solutions architect and software engineer with extensive experience in large-scale, high-load, geographically-distributed systems. I specialise in project rescue, governance and development methodologies.

My main areas of interest are domain-driven design, event sourcing, massively-scalable service architectures and cloud computing.

I'm a regular speaker and presenter at conferences and training events. My mother wrote COBOL on punch cards and I've been coding in one form or another since I was five years old.

Play

Photographer. Ballroom dancer. Cyclist. Motorcyclist. I love my outdoor sports - and anyone who won't dance is chicken.