Skip to content
Dec 12 / Martin

My New Black

New BlackA few days ago, I was minding my own business, writing some bad code, when this tweet caught my attention:

a .NET Sinatra clone in the new black

It made me laugh, actually. In a guilty kind of way. Rob nailed it, it’s true, and you know how I know? Because that bad code I was writing was kind of pretty much exactly my new black.

I wouldn’t argue that we need another web framework, but I would say that a web framework is a cool project to undertake to learn more about how these things work (and how to write them badly).

In the spirit of hacking, I figured I’d try and write a simple web framework that sits on top of all the goodness of ASP.NET. Not having to worry about dealing with the underlying HTTP fundamentals and also having ASP.NET Routing at my disposal, it seemed like a fun thing to do.

I’m writing about it here in case others are thinking of experimenting in a similar fashion, perhaps it may serve as motivation – if I can do it, trust me, you can do it better.

Tinyweb

So, with the single goal of making it as easy as possible to use, I have come up with Tinyweb (although, I can’t help but think that Black would have been a cooler name). And you know the cool thing about making your own framework? You can decide to do it how ever the hell you like, for instance:

To support dependency injection in the simplest way possible, the Tinyweb initialisation call optionally accepts an instance of a StructureMap registry and does the rest itself. Tinyweb also uses Spark for rendering views. Basically, it’s a little opinionated.

That said, you can still create a new IResult to render views using some other view engine, or supply your own version of the HandlerFactory to use another IOC container. Tinyweb hasn’t been locked into one specific way, it’s just my sensible default.

So, how does it work?

The workflow is as simple as I could think to make it. It’s important you understand that this means it could probably be much simpler. Here are the steps for creating a web app using Tinyweb:

  • Create a public class WhateverYouLikeHereHandler with method(s) that match the name of the HTTP verb they respond to. For example, your SignupHandler might have a Get method that renders a view for user signup and a Post method that accepts the user registration data after the form is posted.
  • Make a call to Tinyweb.Init from Application_Start

And, that’s it.

What about routes?

Oh yeah, routes. Routes are determined through the class name too. In the example above, the route to the handler is /Whatever/You/Like/Here. If I innocently created a RickRollHandler, it would be automatically configured to respond to the URL /Rick/Roll.

This is just a convention and can be modified by the creation of a Route field. If I want my RickRollHandler to be much more successful, I might do the following:

public class RickRollHandler
{
    Route route = new Route("Images/AnnaKournikova");

    public IResult Get()
    {
        return Result.Html("Muahahahah.html");
    }
}

Framework Design

Once Tinyweb has be initialised, it is aware of all URLs that the application serves and the type of handler that should be created for each one. Here’s what happens when some honest, innocent guy tries to request our image of Anna Kournikova:

  • An IHandlerFactory is used to create an instance of the handler that has been associated with the URL being requested. If you haven’t set your own HandlerFactory, don’t panic, the DefaultHandlerFactory will be used – which uses Structure Map to create the handler. If you supplied a Registry to the Tinyweb.Init call, this could mean that the creation of the handler also involves the creation of any dependencies.
  • An IHandlerInvoker is then used to call the correct method on the handler. If the request was an HTTP GET, the DefaultHandlerInvoker would be looking for a method called Get. The same goes for POST, PUT and DELETE. And again, if this makes you reach for the little foam stress duck on your desk with a panic of hand crunches, fear not, you can always create your own MyBatshitCrazyHandlerInvoker: IHandlerInvoker that uses star signs to call the right method.
  • Lastly, the method being called on the handler must return an IResult back to the IHandlerInvoker. The IResult essentially just describes a class that must have a ProcessResult method, and some other dull things. Once the IResult is returned, the ProcessResult method is called to obtain the response, and naturally, this is then put on the response stream and sent back to the honest, innocent guy looking for naked pictures of women on the internet.

The only other bit is the scanning of handlers, which is performed during the Tinyweb.Init call. Handlers are those classes that end with the word Handler and have at least one method that corresponds to one of the four HTTP verbs.

Handlers are found by asking the current IHandlerScanner for them. You may be able to guess what I’m about to say next. If you want to customise (more likely optimise) the way that handlers are found, just create your own MyBatshitCrazyHandlerScanner and ask Tinyweb to use that instead. Despite the insanity of such a thing, it will oblige.

OK, I’m Interested

Together with the framework there are a couple of example applications demonstrating things like Spark view rendering and model binding. Feel free to browse around the project and I’d be happy to hear feedback.

There’s no binary release, as I expect the 2 people who care about this have VS2010/.NET4 and can compile it themselves, but if anyone wants to check it out and can’t, let me know and I’ll get a binary version up there too.

EDIT

There is now a binary release, so if you don’t want to compile it yourself you can pull down the latest build from the build server.

Leave a Comment