Nancy from an MVC Developer Point of View, Revision 2

Last update 27 Jun 2013.

I am currently learning Nancy and this is my braindump. If you don’t yet know what Nancy is, Nancy Homepage provides a good explanation. I want to try it because it looks more compositional than ASP.NET MVC.

I have been using ASP.NET MVC since 2010 so when learning Nancy I see it from the point of view of an ASP.NET MVC developer.

I did this on June 2013: current version of Visual Studio is Visual Studio 2012, and the latest stable release of Nancy on Nuget is version 0.17.1.

This is the blog post that inspired me to compare Nancy with MVC.

How to Begin?

  1. Create a new project in Visual Studio using the ASP.NET Empty Web Application template.
  2. Install-Package Nancy.Hosting.Aspnet. This will nuget Nancy as dependency.
  3. Install-Package Nancy.Viewengines.Razor.

The project is ready, containing only a Web.config file.

Where’s My Controller?

In MVC there are controllers. In Nancy there are modules. Here’s a simple module:

// module name is Hello, with 'Module' suffix
public class HelloModule : Nancy.NancyModule
{
    // module constructor is very important in Nancy
    public HelloModule()
    {
        Get["/"] = Index;
        // assign a function to a route 'pattern' to handle requests that match the pattern
        // there are Get and Post collections
    }

    // this is action
    private dynamic Index(dynamic parameters)
    {
        return View["Index"];
    }
}

In MVC controller name ends with ‘Controller’. In Nancy module name ends with ‘Module’.

In MVC all routing rules are defined in ‘App_Start\RouteConfig.cs’. In Nancy each module specifies what kind of route pattern it will handle.

In MVC action’s return type is ActionResult. In Nancy action’s return type is dynamic.

In Nancy there is always only one action parameter, and the parameter type is dynamic.

Source Code Organization

In MVC controllers are in Controllers folder and views are in Views folder. You can copy this organization strategy in Nancy. Continuing the HelloModule example above:

But Nancy allows different source code organization. My favorite is to have one folder for each module (the folder name matches the module name) and put all relevant views and viewmodels into the same folder. For example the HelloModule:

Viewmodel

// viewmodel name ends with 'Model'
public class IndexModel
{
    public string Code { get; set; }
    ...
}

Prepare the viewmodel from an action method:

private dynamic Index(dynamic parameters)
{
    var data = new IndexModel { ... };
    return data;
}

Nancy will look for a razor file with file name that matches the class name of the viewmodel.

For fun, try not to have any razor file to handle an action. This cute little fella will show up:

cute little fella

Nancy.RequestExecutionException: Oh noes! ---> Nancy.ViewEngines.ViewNotFoundException: Unable to locate view 'Index'
Currently available view engine extensions: sshtml,html,htm,cshtml,vbhtml
Locations inspected: ,,,,,,,,views/Hello/Index-en-US,views/Hello/Index,Hello/Index-en-US,Hello/Index,views/Index-en-US,views/Index,Index-en-US,Index

As you can see, Nancy looked for the razor file in a lot of places:

  1. views/Hello/Index-en-US
  2. views/Hello/Index
  3. Hello/Index-en-US
  4. Hello/Index
  5. views/Index-en-US
  6. views/Index
  7. Index-en-US
  8. Index

Razor

Nancy requires this line at the top of a razor file if you want to activate intellisense for viewmodel:

@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<IndexModel>

For example:

@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<IndexModel>
<!DOCTYPE html>
<html>
<body>
    <p>@Model.Code</p>
    ...
</body>
</html>

When adding a HTML file into the project, don’t forget to set the ‘Build Action’ to ‘Content’. The default build action is none so the file will not be included when you publish.

Model Binding

What about model binding? How do I populate a model from query parameters, form data, and request body?

using Nancy.ModelBinding;

public class HelloModule : Nancy.NancyModule
{
    public HelloModule()
    {
        Get["/"] = Index;
    }

    private dynamic Index(dynamic parameters)
    {
        var input = this.Bind<IndexModel>();
        // or
        // IndexModel input = this.Bind();

        ...
    }
}
  1. Add using Nancy.ModelBinding;
  2. Create the class that will accept the parameters.
  3. Within a controller action, call this.Bind.

In MVC it is possible to bind to a list of objects or to a dictionary with query parameters that look like:

list[0].code=A&list[0].name=peanut&list[1].code=B&list[1].name=butter
dict[0].key=X&dict[0].value=fish&dict[1].key=Y&dict[1].value=chips

This is not yet supported in Nancy. So for dictionary or a list of objects, the payload has to be in the request body as JSON or XML.

Viewbag

In MVC there are ViewData and ViewBag. Nancy has ViewBag, but not ViewData. Usage is very similar to MVC:

public class HelloModule : Nancy.NancyModule
{
    public HelloModule()
    {
        Get["/"] = Index;
    }

    private dynamic Index(dynamic parameters)
    {
        this.ViewBag.data = "send some data";
        return View["Index"];
    }
}

In the razor view:

<p>@ViewBag.data</p>

Redirect

using Nancy;

public class HelloModule : Nancy.NancyModule
{
    public HelloModule()
    {
        Get["/"] = Index;
        Get["/newlocation"] = _ => "Shifted!";
    }

    private dynamic Index(dynamic parameters)
    {
        // note the ~ at the beginning
        return Response.AsRedirect("~/newlocation");

        // don't do this because the ~ won't work
        // return new Nancy.Responses.RedirectResponse("~/newlocation");
    }
}

Looks like that is the one and only way to redirect in Nancy. Since routes don’t have names in Nancy, there is no equivalent of MVC’s RedirectToAction.

Session

According to my limited research there is only one built in session mechanism in Nancy. It uses browser cookie instead of server memory (good decision in my opinion). By default it is not enabled. To enable it, the default Nancy bootstrapper needs to be overridden.

public class CustomNancyBootStrapper : Nancy.DefaultNancyBootstrapper
{
    protected override void ApplicationStartup(
        Nancy.TinyIoc.TinyIoCContainer container, Nancy.Bootstrapper.IPipelines pipelines)
    {
        base.ApplicationStartup(container, pipelines);
        Nancy.Session.CookieBasedSessions.Enable(pipelines); // this is the line
    }
}

To use the session:

public class HelloModule : Nancy.NancyModule
{
    public HelloModule()
    {
        Get["/"] = Index;
        Get["/newlocation"] = _ =>
        {
            var sesdata = Session["a"];     // get session data
            Session.Delete("a");            // remove data from session
            return sesdata;                 // will return 'brown fox' to browser
        };
    }

    private dynamic Index(dynamic parameters)
    {
        Session["a"] = "brown fox";         // save to session
        return new Nancy.Responses.RedirectResponse("/newlocation");
    }
}

Tempdata

Looks like there is no TempData equivalent in Nancy for now, so message passing in Post-Redirect-Get scenario should use session.

Master and Partial Page

Razor master page and partial page is available in Nancy. For example there is a layout page in Layout\Base.cshtml containing:

@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>@ViewBag.title</title>
@RenderSection("ExtraHeaders", required: false)
</head>
<body>
@RenderBody()
</body>
</html>

And a razor page in Home\WithMaster.cshtml containing:

@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
@{
    Layout = "Layout/Base.cshtml";      // Use slash instead of backslash
                                        // Don't begin with slash
}

<p>@ViewBag.title</p>

@Html.Partial("SmallPartial")

@section ExtraHeaders{
    <link rel="stylesheet" href="custom.css" />
}

And a partial page in Home\SmallPartial.cshtml containing:

<p>This is from partial</p>

These pages are called from this module:

public class HomeModule : Nancy.NancyModule
{
    public HomeModule()
    {
        Get["/"] = _ =>
        {
            ViewBag.title = "Yeah!";
            return View["WithMaster"];
        };
    }
}

HTML Helpers

Html.ActionLink, Html.BeginForm, Html.TextBox, etc are not available.

But! For creating links, you can just use ~. For example: <a href="~/otherpage">Go there</a>. The ~ can be used in other places too, like the form tag: <form action="~/theurl">.

Filters

Nancy has BeforeRequest, AfterRequest, and OnError. Filters are set up in bootstrapper’s RequestStartup or ApplicationStartup.

UPDATE: Filters can also be defined inside a module and they will apply only for that module (thanks Phillip Haydon!).

public class CustomNancyBootStrapper : Nancy.DefaultNancyBootstrapper
{
    protected override void RequestStartup(
        Nancy.TinyIoc.TinyIoCContainer container, 
        Nancy.Bootstrapper.IPipelines pipelines, 
        Nancy.NancyContext context
    )
    {
        base.RequestStartup(container, pipelines, context);
        
        // hooking up filters
        pipelines.BeforeRequest += CheckSomething;
        pipelines.AfterRequest += ModifyResult;
        pipelines.OnError += HandleThisError;
    }

    protected override void ApplicationStartup(
        Nancy.TinyIoc.TinyIoCContainer container, Nancy.Bootstrapper.IPipelines pipelines)
    {
        base.ApplicationStartup(container, pipelines);

        // where to put the filter? in RequestStartup or ApplicationStartup?
        // example: if in the filter you need a database session object
        // that is request scoped, put it in RequestStartup

        pipelines.BeforeRequest += ...
    }

    // example of request filter
    private Nancy.Response CheckSomething(Nancy.NancyContext ctx)
    {
        if (ctx.Request.Headers["Magic-Word"].FirstOrDefault() != "Please")
        {
            // if you return a response object, nancy will not proceed to modules
            return new Nancy.Response { StatusCode = Nancy.HttpStatusCode.BadRequest };
        }
        // if you return null, nancy will proceed to module
        return null;
    }

    // example of response filter. Not returning anything
    private void ModifyResult(Nancy.NancyContext ctx)
    {
        // you can modify response
        ctx.Response.Headers["X-Powered-By"] = "Nancy";
    }

    // example of error filter
    private Nancy.Response HandleThisError(Nancy.NancyContext ctx, Exception ex)
    {
        return null;
    }
}

Example of a filter inside a module:

public class HomeModule : Nancy.NancyModule
{
    public HomeModule()
    {
        Get["/"] = _ => "Hello!";
        Before += ctx => null;      // a Before filter
    }
}

Error Handling

Unhandled module errors can be caught with OnError filter.

Returning a HTTP error from a module can be done with something like this:

public class HomeModule : Nancy.NancyModule
{
    public HomeModule()
    {
        Get["/"] = _ =>
        {
            return new Nancy.Response { StatusCode = Nancy.HttpStatusCode.NotFound };
        };
    }
}

Require HTTPS

Can be implemented as BeforeRequest filter.

Output Cache, Generic Cache

As far as I know, there is no built in one. But there is a sample code on how to implement output cache.

Returning Files / Binary

In MVC, there is a ‘File’ response type. It is used like this:

return File(output, "application/pdf");

Here is how to do that in Nancy (source):

public class HomeModule : Nancy.NancyModule
{
    public HomeModule()
    {
        Get["/"] = _ =>
        {
            var file = new Nancy.Response();
            file.Headers["Content-Disposition"] = "attachment; filename=afile.pdf";
            file.ContentType = "application/pdf";
            file.Contents = str =>
            {
                using (var writer = new System.IO.StreamWriter(str))
                {
                    writer.Write( ... );
                };
            };
            return file;
        };
    }
}

Comments


The Many Ways to Call Linq GroupBy

Last update 25 Jun 2013.

I keep forgetting Linq’s GroupBy syntaxes. Maybe if I write it down I will remember it better.

First, let’s set up the playing field.

public class Order
{
    public Customer Customer { get; set; }
    public List<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public Product Product { get; set; }
    public long Quantity { get; set; }
    public long UnitPrice { get; set; }
}

public class Product
{
    public string Name { get; set; }
}

public class Customer
{
    public string Name { get; set; }
}

From the data structure above, I want to get a list of customers (non repeating) with the total sales of each customer.

I see that there are 3 general ways to call GroupBy:

Let’s try the easiest way first: just tell GroupBy how to divide the collection into groups.

var orders = new List<Order>();

var justSplitIntoGroups = orders.GroupBy(
    o => o.Customer
).Select(
    g => new
    {
        customer = g.Key,
        sales = g.Sum(
            o => o.Items.Sum(
                i => i.Quantity * i.UnitPrice
            )
        )
    }
);

Another way: tell GroupBy to first transform the original collection.

var splitAndTransformOriginal = orders.GroupBy(
    o => o.Customer,
    o => o.Items.Sum(
        i => i.Quantity * i.UnitPrice
    )
).Select(
    g => new
    {
        customer = g.Key,
        sales = g.Sum()
    }
);

Another way: tell GroupBy how to construct the end result.

var splitAndReduce = orders.GroupBy(
    o => o.Customer,
    (cust, ordrs) => new
    {
        customer = cust,
        sales = ordrs.Sum(
            o => o.Items.Sum(
                i => i.Quantity * i.UnitPrice
            )
        )
    }
);

The completest approach: tell GroupBy how to transform the original collection and how to construct the end result.

var splitTransformReduce = orders.GroupBy(
    o => o.Customer,
    o => o.Items.Sum(
        i => i.Quantity * i.UnitPrice
    ),
    (cust, sls) => new
    {
        customer = cust,
        sales = sls.Sum()
    }
);

Comments


Setting Up RavenDB in Nancy

Last update 24 Jun 2013.

I’ve been dabbling with Nancy and RavenDB and here is my implementation notes when trying to find the best way to initialize RavenDB’s DocumentStore and DocumentSession in Nancy.

My inspiration was this blog post. The guiding principle is to rely as much as possible on Nancy’s TinyIoC.

First I put the connection settings into the project’s config file.

<connectionStrings>
    <add name="RavenHQ" connectionString="Url=https://ibis.ravenhq.com/databases/abc-def;ApiKey=ffffffff-ffff-ffff-ffff-ffffffffffff" />
</connectionStrings>

So I could create DocumentStore with something like this.

var docstore = new Raven.Client.Document.DocumentStore
{
    ConnectionStringName = "RavenHQ"
};

But since later I want to encrypt the API key, I moved the connection data to appSettings.

<appSettings>
    <add key="RavenHQURL" value="https://ibis.ravenhq.com/databases/abc-def" />
    <add key="RavenHQAPIKey" value="ffffffff-ffff-ffff-ffff-ffffffffffff" />
</appSettings>

Next, I created a custom bootstrapper.

using System.Configuration;

public class MyNancyBootStrapper : Nancy.DefaultNancyBootstrapper
{
    protected override void ConfigureApplicationContainer(
        Nancy.TinyIoc.TinyIoCContainer container)
    {
        base.ConfigureApplicationContainer(container);
        container.Register<Raven.Client.IDocumentStore>(GenerateRavenDocStore());
    }

    private Raven.Client.IDocumentStore GenerateRavenDocStore()
    {
        var ravenurl = ConfigurationManager.AppSettings["RavenHQURL"];
        var ravenkey = ConfigurationManager.AppSettings["RavenHQAPIKey"];

        var docstore = new Raven.Client.Document.DocumentStore
        {
            Url = ravenurl,
            ApiKey = ravenkey
        };
        docstore.Initialize();

        return docstore;
    }

    protected override void ConfigureRequestContainer(
        Nancy.TinyIoc.TinyIoCContainer container, Nancy.NancyContext context)
    {
        base.ConfigureRequestContainer(container, context);
        container.Register<Raven.Client.IDocumentSession>(GenerateRavenSession(container));
    }

    private Raven.Client.IDocumentSession GenerateRavenSession(
        Nancy.TinyIoc.TinyIoCContainer container)
    {
        var store = container.Resolve<Raven.Client.IDocumentStore>();
        var session = store.OpenSession();
        return session;
    }
}

ConfigureApplicationContainer is overridden to register IDocumentStore with application scope (one instance of IDocumentStore for the entire application). Generation of the DocumentStore is handled by the factory method GenerateRavenDocStore. Later I will decrypt the encrypted API key within GenerateRavenDocStore.

ConfigureRequestContainer is also overridden to register IDocumentSession with request scope (one instance of IDocumentSession for each HTTP request).

With this setting, I can use IDocumentSession in a module like so.

public class MyModule : Nancy.NancyModule
{
    private Raven.Client.IDocumentSession DB;

    public MyModule(Raven.Client.IDocumentSession session)
    {
        // save IDocumentSession in a private instance variable
        DB = session;
        
        Get["/"] = HomeIndex;
    }

    private dynamic HomeIndex(dynamic parameters)
    {
        ...
        var usr = new User { Name = "Agus" };
        
        // use IDocumentSession
        DB.Store(usr);
        DB.SaveChanges();
        ...
    }
}

IDocumentSession is one of the module’s constructor arguments. In the constructor, IDocumentSession is saved to an instance variable so it is available to route handlers.

I prefer to call SaveChanges explicitly from within route handlers. Another option is to call SaveChanges automatically at the end of each request by using after request hook. Maybe with something like this.

// this is the module's constructor
public MyModule(Raven.Client.IDocumentSession session)
{
    DB = session;
    Get["/"] = HomeIndex;

    // adding an after request hook
    After += ctx =>
    {
        DB.SaveChanges();
        // or check first whether there are data changes or errors before calling SaveChanges
    };
}

Comments


Schneier Membahas Internet

Last update 22 Jun 2013.

Russ Roberts mengundang Bruce Schneier untuk berbicara di talk show-nya mengenai perebutan kekuasaan di internet.

Awalnya internet membantu pihak yang ditindas sebagai sarana komunikasi, dll. Pihak penguasa tidak terlihat memanfaatkan internet karena sifatnya yang birokratis dan lamban mengikuti perubahan. Tapi sekarang pihak penguasa sudah mendapat cukup waktu untuk mulai menggunakan internet.

Cuplikan-cuplikan menarik:

Diskusi yang menarik, highly recommended!

Comments


Setting Up Nancy Forms Authentication

Last update 21 Jun 2013.

This is my implementation notes when I was setting up forms authentication in a web app project that uses Nancy.

Nancy forms authentication allows user to login with username and password. It then converts this username and password into a token, and then sends the token into user’s browser as a cookie. The next time user sends a request to the Nancy server, the cookie will be included in the request. Nancy can then deduct the user from the token, and allow or deny access to the page.

First step is to nuget Nancy forms authentication:

Install-Package Nancy.Authentication.Forms

Next, implement the Nancy.Security.IUserIdentity interface. Simplest one is like this:

public class MyUserIdentity : Nancy.Security.IUserIdentity
{
    public string UserName { get; set; }
    public IEnumerable<string> Claims { get; set; }
}

But most likely you need to implement this interface on your already existing user class.

Next, implement the Nancy.Authentication.Forms.IUserMapper interface. This is used to determine the user from a received token. If you don’t yet have a way to determine user from a GUID, you need to create that mechanism before you can implement this interface.

public class MyUserMapper : Nancy.Authentication.Forms.IUserMapper
{
    public Nancy.Security.IUserIdentity GetUserFromIdentifier(
        Guid identifier, Nancy.NancyContext context)
    {
        var data = ... get user object from database based on received GUID (identifier) ... ;
        return data;
    }
}

Next, create a custom bootstrapper.

public class MyBootStrapper : Nancy.DefaultNancyBootstrapper
{
}

Optional step: override ConfigureRequestContainer to set IUserMapper lifecycle to one instance per HTTP request. This is recommended if your database session lifecycle is also one instance per HTTP request.

public class MyBootStrapper : Nancy.DefaultNancyBootstrapper
{
    protected override void ConfigureRequestContainer(
        Nancy.TinyIoc.TinyIoCContainer container, Nancy.NancyContext context)
    {
        base.ConfigureRequestContainer(container, context);
        container.Register<Nancy.Authentication.Forms.IUserMapper, MyUserMapper>();
    }
}

Next, override RequestStartup. After the standard RequestStartup, add a call to FormsAuthentication.Enable.

public class MyBootStrapper : Nancy.DefaultNancyBootstrapper
{
    protected override void RequestStartup(
        Nancy.TinyIoc.TinyIoCContainer container,
        Nancy.Bootstrapper.IPipelines pipelines,
        Nancy.NancyContext context
    )
    {
        base.RequestStartup(container, pipelines, context);
        Nancy.Authentication.Forms.FormsAuthentication.Enable(
            pipelines,
            new Nancy.Authentication.Forms.FormsAuthenticationConfiguration()
            {
                RedirectUrl = "~/login",
                UserMapper = container.Resolve<Nancy.Authentication.Forms.IUserMapper>()
            }
        );
    }
}

Next, add a route so there’s something in “~/login” (see RedirectUrl in FormsAuthenticationConfiguration above).

public class LoginModel
{
    public string username { get; set; }
    public string password { get; set; }
}

public class LoginModule : Nancy.NancyModule
{
    public LoginModule()
    {
        Get["/login"] = LogIn;
    }

    public dynamic LogIn(dynamic parameters)
    {
        var data = new LoginModel();
        return data;
    }
}
<form method="post">
    <label>username</label><input type="text" name="username" />
    <label>password</label><input type="password" name="password" />
    <button type="submit">Login</button>
</form>

Next, add a route to handle user login request. This method is responsible to check whether the password matches the username. If user provides a correct combination, find out the user’s GUID from provided username and password, then call LoginAndRedirect (or LoginWithoutRedirect). LoginAndRedirect will send a cookie to user’s browser.

using Nancy.Authentication.Forms;

public class LoginModule : Nancy.NancyModule
{
    public LoginModule()
    {
        Post["/login"] = LogInPost;
    }

    public dynamic LogInPost(dynamic parameters)
    {
        var username = (string)this.Request.Form.username;
        var password = (string)this.Request.Form.password;

        var user =  ... get user from DB based on username ... ;

        if ( ... username matches password ... )
        {
            var token =  ... get user's GUID ... ;
            return this.LoginAndRedirect(token);
        }
        else
        {
            throw new ArgumentException("Invalid username or password");
        }
    }
}

Next, add a route to handle logout requests. Within the method call LogoutAndRedirect. Then provide links to this in the HTML.

using Nancy.Authentication.Forms;

public class LoginModule : Nancy.NancyModule
{
    public LoginModule()
    {
        Get["/logout"] = LogOut;
    }

    public dynamic LogOut(dynamic parameters)
    {
        return this.LogoutAndRedirect("~/");
    }
}

Next, in modules that require authentication, add a call to RequiresAuthentication.

public class VIPModule : Nancy.NancyModule
{
    public VIPModule()
        : base("/vip")
    {
        this.RequiresAuthentication();
        Get["/"] = _ => "VIP only!";
    }
}

Aaand you’re done!

Comments