Alexander Beletsky's development blog

My profession is engineering

Customizing Folders Layout for NancyFX Application

NancyFX applications are full of conventions. The default conventions are good enough, especially if you use ASP.NET hosting, where you probably don’t even care. If you plan to do self-hosting application, you typically want to change some bits.

In my case, during Candidate application re-writing, I wanted to have some custom folder layout, that would be clean and not confuse application users with so many details. In fact, I wanted something that in deployed state looks like this:


folder layout

‘Bin’ folder would contain the application itself, as well as all referenced assemblies. ‘Client’ folder would contain all client side code - HTML/CSS/Javascript. Initially I thought to make all of those as embedded resources, but firstly did not worked on Nancy v.0.11.0, secondly having those files placed separately make it possible to easy application updates (patching).

Fortunately, it is easy to apply any folder layout you want. There are 2 types of recourses that Nancy is looking from outside, they are - views and static content. Both of them are resolved by default conventions and those conventions applied against the application root.

Changing the application root

In my case, executables are places in Bin folder, but resources are placed one level above. By default, the application root is Environment.CurrentDirectory and I need to changed that. In order to make this happen, you have to implement IRootProvider instance.

namespace Candidate.Nancy.Selfhosted.App
{
    public class PathProvider : IRootPathProvider
    {
        public string GetRootPath()
        {
            return Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\"));
        }
    }
}

There is a little pitfall, that I got into. Please, make sure that root folder is absolute path (Path.GetFullPath - returns absolute path). If you didn’t do that, some part of application could not work right. I’ve sent a pull request for StaticContentConventionBuilder class, but more appropriate fix is actually having it rooted.

After, just place the type of path provider instance into Nancy bootstrapper class, as overridable property:

protected override Type RootPathProvider
{
    get
    {
        return typeof(PathProvider);
    }
}

Changing the default conventions

Now, we have to teach Nancy to look for view and static resources right.

The bootstapper contains corresponding virtual method, called ConfigureConventions. There I’ll overrider the default ones, like that:

protected override void ConfigureConventions(NancyConventions nancyConventions)
{
    // static content
    nancyConventions.StaticContentsConventions.Clear();
    nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("scripts",
                                                                                                "Client/scripts"));
    nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("content",
                                                                                                "Client/content"));
    // view location
    nancyConventions.ViewLocationConventions.Clear();
    nancyConventions.ViewLocationConventions.Add((viewName, model, context) => string.Concat("Client/views/", viewName));
    nancyConventions.ViewLocationConventions.Add((viewName, model, context) => string.Concat("Client/views/", context.ModuleName, "/", viewName));
}

For view location conventions you just need to provider the folder (relative to rootfolder), where the view are located. I used only 2 simple conventions: 1) view is just placed under /views folder 2) view is placed under /view/moduleName folder.

For static resources, it a little bit trickier. You have to return the *response* object, for corresponding request. Fortunately, StaticContentConventionBuilder class, has some helper methods that make it simpler.