This post is over 6 months old. Some details, especially technical, may have changed.

Micro Web Frameworks in .NET 101: Jessica

Now I have got the opinion piece out of the way I can start the bit I like - dissecting the various technologies that could be classed as micro web frameworks. First on the table is Jessica.

Getting Started - Hello World

Getting a simple Hello World set up in Jessica involves (optionally) taking more out than you put in. Jessica requires very little configuration so creating a new web project, deleting all the extra files and folders, trimming the web.config and emptying the Global.asax.cs makes for a nice blank canvas to start with.

Next step is pretty difficult :-). Within the NuGet Package Manager Console just install the Jessica package

Install-Package Jessica

Once installed be need to tell Jess to initialise/bootstrap itself when the application starts by calling the initialise method in the Application_Start method of the Global.asax.cs

public class Global : System.Web.HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        Jess.Initialise();
    }
}

At this point it's also possible to supply a number of configuration options but we will touch on this later along with the optional configuration XML. Now that Jess is setup to configure itself all we need to do now is define the module that will handle our complex Hello World output.

Modules form the basis of Jessica each one holding a number of routes (based on URL's and HTTP verbs) and associated actions. Our app will have a single module,

public class MainModule : JessModule
{
    public MainModule()
    {
        Get("/", r => "Hello World");
    }
}

This simple module defines a single route that will match any standard GET request for the root of the app. And that's it. When you run the app Jess will inspect the assembly and find all the modules and wire up the routes.

The Rest of the Pie

Obviously serving plain bits of text is a bit useless so there are plenty of things that Jessica offers that makes it, well, actually useful.

Configuration

Jessica can be configured programatically or via XML config. A number of things you can currently set include,

  • Environment - the environment you build in e.g. development, test, release. Default development
  • Public Directory - The directory to be used to server static content (html, js, css, images) that require no preprocessing. Default public
  • Views Directory - The directory to be used to serve views that can be dynamically rendered (Razor, Textile, Markdown, Liquid etc.). Default views

The XML config is outlined below but each of these properties can be set in the Global.asax.cs programatically as well via the Jess.Configuration properties.

<configSections>
    <section name="jessica"
             type="Jessica.Configuration.JessicaConfiguration,Jessica"/>
</configSections>

<jessica environment="development"
         publicDir="Public"
         viewsDir="Views"/>

Routes and Actions

Routes may be defined as a literal path such as /users or /store/toys or with variable segments such as /user/:id or /blog/:year/:month/:day. Variable segments are then parsed out of the route url and the values added to the action argument.

Get("/blog/:year/:month/:day", r =>
{
    var blog = Blog.Get(r.year, r.month, r.day);
    return View("blog", blog);
});

That seems to be about the extent of the route manipulation for now. Would be nice to see the other Sinatra stuff included such as splats, regular expressions and conditions.

Actions are fairly simple they take a dynamic object and are expected to return a Jessica Response object. The dynamic object passed in contains a number of things such as route parameters, querystring parameters and the current HttpContext. As an added niceness the Jessica Response object has a number of implicit type conversions that allow us to return simpler objects

public MainModule()
{
    // string -> text/html
    Get("/", r => "Hello World");

    // int -> http status code
    Get("/", r => 404);

    // Action<Response> -> Response stream
    Get("/", r => Response.AsText("Test"));
}

Static Content

Jessica allows us to return static content simply. First of all all routes are first checked for the existence of a file at that URL. If a file is found it is served, if not then Jessica goes to the route table and works it's magic. Putting resources in the publicDir allows Jessica to serve static content from the route actions and the Response object comes with a number of nice helpers to help make your static actions neater.

public MainModule()
{
    Get("/", r => Response.AsCss("test.css"));
    Get("/", r => Response.AsFile("trojanz.exe", "application/application"));
    Get("/", r => Response.AsHtml("test.html"));
    Get("/", r => Response.AsJs("test.js"));
    Get("/", r => Response.AsJson(new { Name = "James", Age = 31 }));
    Get("/", r => Response.AsRedirect("http://www.google.com"));
    Get("/", r => Response.AsText("test"));
}

Views

What web technology would be complete without the ability to serve non-static views? Oddly enough Jessica, out of the box at least, comes with no view engines. I suspect this is because dynamic view rendering isn't technically something that should form the basis of a web framework - thats the job of the view engines. Jessica does however come with an extension point of adding X amount of view engines - Jess.ViewEngines. There is also a healthy volume of view engines available for Jessica,

Rendering a view from an action is also very simple

public class MainModule : JessModule
{
    public MainModule()
    {
        Get("/user/", r => View("index"));
        Get("/user/:username", r => View("user", r.username));
    }
}

The first option renders a simple view called index while the second takes a model object that is pushed into the scope of the view and can be used to build the view. Jessica will loop through it's ViewEngines collection and attempt to resolve the view name to a file in the specified viewsDir folder. If it finds a matching view it will render it.

I am going to take the lead from Jessica itself and not dive into the various view choices for now. They are pretty self explanatory anyway.

Summing Up

So thats the 101, the skinny, the lowdown, the starter for 10, the basics and other random statements of Jessica. Hopefully this covers the vast majority of what v0.83 of Jessica offers. It's a nice minimla framework that attempts to stick to Sinatra's philosophy. It would be nice to see a few more of Sinatras features creeping in especially route conditions. There is also some nice high level error handling being introduced (for v0.9 I assume) for handling application wide errors and 404's. I plan on talking about these when they make an appearance.

Corrections, comments and criticisms below please.

Published in .NET on May 18, 2011