I’ve had a very enlightening last few weeks. I’m talking about the explosion in the number of alternative web frameworks for .NET and especially the emergence of alternatives to our old friend, the core ASP.NET infrastructure. Although a lot of this work has been around for a while, I’ve only just become aware of it.
The world of open source .NET frameworks has been vibrant for some time now. We’ve had Monorail for years and more recently FubuMvc and OpenRasta. So far OpenRasta is the only one which has attempted to break away from the stranglehold of ASP.NET and build it’s own HTTP abstraction.
With OpenRasta as the exception, all the existing frameworks (including ASP.NET MVC) have been targeted at ‘traditional’ web applications. By that, I mean applications where the majority of formatting and execution occurs on the server and to the browser the application appears as little more than static HTTP. But all this is changing. Two things are radically altering the development landscape; HTML5 and Mobile apps. both of these require infrastructure optimised to build scalable RESTful JSON APIs. We also need frameworks that will deploy without requiring an IIS instance and that preferably work on Linux with Mono.
Elsewhere, in the world of open source dynamic languages, things have been moving very fast. Node.js and Sinatra have generated enormous interest, as have the flexibility of web middleware abstractions like Rack and WSGI.
With these as inspiration, new .NET web frameworks have been popping up all over the place. Even more exciting is the emergence of common abstractions, so that we may be able to assemble web application stacks like Lego. Read on…
Manos
The first new framework that came to my attention a few weeks back was Manos by Jackson Harper. This is a lightweight web framework for Mono inspired by node.js and Sinatra. It uses the same IO library, libev, as node.js. The Herding Code podcast interviewed Jackson recently. It’s well worth a listen.
Here’s a snippet of a simple Manos application:
public class HelloWorld : ManosApp {
public HelloWorld ()
{
Get ("/", ctx => ctx.Response.End ("Hello, World"));
}
}
As you can see there’s a very simple mapping of a route to a delegate. In this case the path ‘/’ maps to a delegate that returns “Hello World” and then completes. Because the context is passed as a an argument to the delegate that handles the request, it’s trivial write asynchronous applications. In the podcast Jackson claimed that he can handle 10,000 open connections in his tests, that’s impressive.
Nancy
The next framework to come to my notice was Nancy by Andreas HÃ¥kansson. This framework is heavily influenced by Sinatra. Currently it only works with ASP.NET, but the intention is to make it server agnostic. Here’s a snippet:
public class Module : NancyModule
{
public Module()
{
Get["/"] = parameters => {
return "Hello World";
};
}
}
Nancy has a dictionary that maps routes to delegates and the return value of the delegate dictates the response.
Nina
Around about the same time I also became aware of Nina by Dotan Nahum. Nina is also inspired by Sinatra (do you get the name references?). Nina is based on ASP.NET, I couldn’t see if there was any intention of making it work on other platforms. Here’s Nina’s hello world:
public class HelloWorld : Nina.Application
{
public HelloWorld()
{
Get("/", (m,c) => Text("Hello World"));
}
}
Again we see the same pattern, mapping a route to a delegate. This time the delegate takes NameValueCollection of parameters and the HttpContext as its arguments and returns the response. Nina seems a little more developed than Nancy. It’s got full integration for most of the existing .NET view engines for example.
Kayak
Just yesterday I listened to Benjamin van der Veen talking to Scott Hanselman on Hanselminutes about Kayak. Kayak, like Manos is a full web stack and I understood from the interview that it also uses, or will use, libev. Kayak, like the other frameworks here has a direct route to method mapping:
public class MyService : KayakService
{
[Path("/")]
public void Root()
{
Response.Write("Hello, world.");
}
}
OWIN
OWIN stands for Open Web Interface for .NET. This is even more exciting than the explosion in innovation around web frameworks itself. It’s a standard interface between servers and frameworks, currently under intense discussion at the .NET HTTP Abstractions group. This is important, because once it becomes an adopted standard it will mean that any server should work with any framework. So for example, I will be able to run OpenRasta on libev, or maybe Manos on IIS.
It will enable separate innovation in severs and frameworks, so you’ll be able to take a high performance scalable sever and marry it with your framework of choice. More than that, it’ll enable independent development of web middleware. Say I want to write a compression library, I won’t have to choose a particular framework to host it in, I simply make it talk OWIN at both ends and it will plug into any combination of framework and sever.
Here is the interface as it currently stands, but remember it’s under intense development at the moment and is likely to change:
namespace Owin
{
/// <summary>
/// An HTTP application.
/// </summary>
public interface IApplication
{
/// <summary>
/// Begins the asynchronous process to get the <see cref="IResponse"/>.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="callback">The callback.</param>
/// <param name="state">The state.</param>
/// <returns>The <see cref="IAsyncResult"/> that represents the asynchronous invocation.</returns>
IAsyncResult BeginInvoke(IRequest request, AsyncCallback callback, object state);
/// <summary>
/// Ends the asynchronous process to get the <see cref="IResponse"/>.
/// </summary>
/// <param name="result">The result.</param>
/// <returns>The <see cref="IResponse"/>.</returns>
IResponse EndInvoke(IAsyncResult result);
}
/// <summary>
/// The HTTP request provides the requested method, uri, headers, application items, and input body.
/// </summary>
public interface IRequest
{
/// <summary>
/// Gets the request method.
/// </summary>
string Method { get; }
/// <summary>
/// Gets the requested uri.
/// </summary>
string Uri { get; }
/// <summary>
/// Gets the headers.
/// </summary>
/// <remarks>
/// Each header key may have one or more values matching the HTTP spec.
/// Example:
/// GET / HTTP/1.0
/// Accept: text/html;application/xml
///
/// Generates a headers dictionary with key "Accept" containing string "text/html;application/xml"
/// </remarks>
IDictionary<string, IEnumerable<string>> Headers { get; }
/// <summary>Gets the application-specific items or settings.</summary>
IDictionary<string, object> Items { get; }
/// <summary>
/// Begins the asynchronous read from the request body.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="count">The count.</param>
/// <param name="callback">The callback.</param>
/// <param name="state">The state.</param>
/// <returns>An IAsyncResult that represents the asynchronous read, which could still be pending.</returns>
/// <see href="http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx"/>
IAsyncResult BeginReadBody(byte[] buffer, int offset, int count, AsyncCallback callback, object state);
/// <summary>
/// Ends the asynchronous read from the request body.
/// </summary>
/// <param name="result">The result.</param>
/// <returns>The number of bytes returned from the stream.</returns>
/// <see href="http://msdn.microsoft.com/en-us/library/system.io.stream.endread.aspx"/>
int EndReadBody(IAsyncResult result);
}
/// <summary>
/// The HTTP response provides the status, headers, and body from an <see cref="IApplication"/>.
/// </summary>
public interface IResponse
{
/// <summary>
/// Gets the status code and description.
/// </summary>
/// <remarks>The string should follow the format of "200 OK".</remarks>
string Status { get; }
/// <summary>
/// Gets the headers.
/// </summary>
/// <remarks>
/// Each header key may have one or more values matching the HTTP spec.
/// Example:
/// HTTP/1.1 200 OK
/// Set-Cookie: foo=bar
/// Set-Cookie: baz=quux
///
/// Generates a headers dictionary with key "Set-Cookie" containing string ["foo=bar";"baz=quux"]
/// </remarks>
IDictionary<string, IEnumerable<string>> Headers { get; }
/// <summary>
/// Gets the body <see cref="IEnumerable<object>"/>.
/// </summary>
/// <returns>The response as an <see cref="IEnumerable<object>"/>.</returns>
/// <remarks>
/// The <see cref="IEnumerable<object>"/> is not guaranteed to be hot.
/// This method should be considered safe to generate either a cold or hot enumerable
/// so that it _could_ be called more than once, though the expectation is only one call.
/// </remarks>
IEnumerable<object> GetBody();
}
}
Read the spec for the interface here: http://owin.github.com/owin/
Like I said, these are very interesting times. Keep an eye on these projects, I expect we’ll all be building web applications very differently in a few years time.