Published: 26 Feb 2010
By: Benjamin van der Veen
Download Sample Code

Kayak is a lightweight HTTP server for the CLR, and the Kayak Framework is a utility for mapping HTTP requests to C# method invocations. With Kayak, you can skip the bulk, hassle, and overhead of IIS and ASP.NET. Kayak enables you to do more with less syntax, and is easy to configure to work in any way you care to dream up.

Contents [hide]

In the beginning

The early days of creating web applications with .NET had us beating our brains against ASP.NET. Developing web applications using ASP.NET is difficult and messy because it tries to shield you from the details of HTTP. In practice, it's awkward to use and it flagrantly abuses HTTP and web browsers. Kayak takes the opposite tack - it's a small, simple, nimble layer between you and the underlying TCP stream which gives you fine-grained control over how your application communicates with its clients.

A word about architecture

Kayak consists of two DLL libraries: a request framework, and the HTTP server on which it depends. Unlike IIS, the Kayak server does not load your code into its process space. You write your own entry point, instantiate a KayakServer object, set it up however you want, and start it when you're ready. Usually, you'll set it up to handle requests using the Kayak Framework.

The framework simply maps HTTP requests to .NET method invocations. Arguments to a method invocation can be generated from the HTTP request, and the return value of the invocation can be used to generate the HTTP response. By default, the Kayak Framework converts .NET objects to and from JSON. It automatically deserializes JSON in the body of an incoming HTTP request and passes the deserialized .NET objects as arguments to invocations of your methods. When your function returns, Kayak then serializes the return value of the invocation as JSON to the body of the HTTP response. Kayak makes it very easy to create those simple HTTP APIs which are all-the-rage these days.

By the following principles of REST (excuse the buzzword; I don't like it either), you eliminate the need for many of the features that bloat tools like ASP.NET, MonoRail, and ASP.NET MVC, as well as Django and other popular frameworks for dynamic languages.

By way of example

We're going to get familiar with Kayak by making a simple CMS API. An in-browser AJAX app or a desktop app in could communicate with this API to implement a management interface for the CMS. Our CMS will implement the concept of a uniquely-identifiable document, each of which has several revisions. A document revision has a title, a body, a slug (URL-safe title), and a list of tags. The full source of the CMS is included with this article as a ZIP file.

Make a server

Before we get further into the details of the CMS, let's take a look at how to set up a Kayak server.

As you can see, this is a regular C# entry point. There is no magical loading of your code into a freaky container that does God-only-knows-what before your code gets control, and when and how when your program exits is very clear. You are in control and can do exactly what you want, when you want to do it. Not at all like the ASP.NET model!

Use the framework

The UseFramework() method in the previous listing is an extension method to the server declared by the framework. It searches your assembly for methods marked with a special attribute called [Path()], and configures the server to respond using those methods. The [Path()] attribute takes a single argument: a string specifying the path which, when requested, will cause the method to be invoked. Let's write a method which will respond to requests for /, the root path, by simply saying hello.

Nothing too surprising there. Notice that our class extends from KayakService. This isn't strictly necessary, but it's pretty handy because it gives us access to the current HTTP context objects. In this example, we're using the KayakResponse object to write some text to the in-memory response buffer. After the method is invoked, Kayak will automatically write the contents of the buffer to the network.

Spit out some JSON

How about something a bit more tricky? Let's create a method which will return a JSON array of all document revisions in our database which have a certain tag.

There are a few interesting things going on here. First, take a look at the [Path()] attribute: we have a nice, REST-ful path to invoke this method. The tag used in the search is taken right from the path and passed into the method invocation. Second, the includeOldRevisions parameter is taken from either the query string or a cookie value. If it's not found in either of those places, it defaults to default(bool) (false). Lastly, the array of DocumentRevision objects that this method returns is automatically serialized to JSON. If you make a request for /documents/tagged/cool, you'll get a JSON array of all the revisions tagged with "cool", which might look something like this:

Eat some JSON

Now let's put JSON data into our app. We want to write an API method for creating a new document. A document is really just a set of revisions, so all we need to make a new document a first revision. Our API method will accept a first revision, save it, inform the client that the operation was a success (using the 201 Created response code), and return the ID of the newly created document as a JSON string. So, let's write a method that accepts a single argument of type DocumentRevision:

We've got two new attributes, [Verb()] and [RequestBody]. First, [Verb()]: it takes a single string argument specifying the HTTP request method which must be used in combination with the path in order for the method to be invoked.

(Kayak uses the not-quite-correct terminology "verb" to refer to an HTTP request method. This is for the sake of distinction from .NET's concept of a method. When no [Verb()] attribute is present on a method, [Verb("GET")] is implied.)

HTTP verbs

Verbs are an important concept in HTTP and understanding the way they're used is important when creating a proper HTTP API, regardless of whether or not you're using Kayak. In general, verbs act on a "resource" on the web server named by the path. There are four standard HTTP verbs: GET, POST, PUT, and DELETE. An HTTP client uses GET to retrieve a resource; an HTTP server sends the requested resource in the body of a response to a GET request. The verbs POST and PUT are used to create or modify a resource: when making a PUT or POST request, an HTTP client sends some data to the server in the body of the HTTP request. The meaning of a POST request is: "Save this data, give it a unique URL, and let me know what that URL is". The meaning of a PUT request is: "Save this data at this URL." Thus POST is most appropriate for creating records, and PUT is most appropriate for updating them. Finally, there is DELETE, which causes the server to remove the resource. An HTTP server should not generate an error if DELETE is invoked on resource more than once.

In the listing above, we've marked the DocumentRevision method parameter with the [RequestBody] attribute to indicate that Kayak should read the request body data sent by the client to construct arguments. The type of the parameter enforces a structure on data accepted from the client.

To invoke this method, a client could send a request such as the following:

Kayak's response will look something like:

The JSON string in the response body indicates the ID of the newly-created document, from which a client, if it knows the API, can construct the URL of the newly created resource.

Not quite what you want? Do something entirely different!

What you've just seen is the "sensible default" configuration of the Kayak Framework. At its core, the Kayak Framework is just about invoking methods in response to HTTP requests. If you would rather read and write XML, you could plug it right in. If you'd rather not mess with attributes and instead map methods based on the names of the enclosing class and the method, you can do that too. The Kayak Framework is designed to be extremely flexible.

Philosophy and other interesting bits

Kayak does not try to be a "full stack" web development platform. As an implementation of HTTP, it does not have any opinions about database layers or template languages. The project aims to foster a deeper understanding of HTTP and an appreciation of it as a simple, versatile, and powerful application protocol.

Kayak's architecture is designed with performance in mind. All disk and network IO is performed in an asynchronous manner, and Kayak can simultaneously service multiple connections per worker thread. When used properly, it enables you to write extremely efficient applications.

In summary

Kayak is a simple server with an easy-to-use request framework. It automatically maps HTTP verb/path combinations to your methods, deserializes arguments to invocations of those methods from the query string or JSON request body, and serializes the return values as JSON. It's behavior is highly configurable, yet simple to use and understand thanks to its limited scope.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Benjamin van der Veen

I am a software developer at Spotlight Mobile in rainy Portland, Oregon, where I design and build user interfaces for the web and iPhone OS, and write application backends in C# using Kayak.

View complete profile here.

Other articles in this category


Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
jQuery Mobile ListView
In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
Exception Handling and .Net (A practical approach)
Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
JQuery Mobile Widgets Overview
An overview of widgets in jQuery Mobile.
Book Review: SignalR: Real-time Application Development
A book review of SignalR by Simone.

You might also be interested in the following related blog posts


Generate stunning ASP.NET/AJAX applications with Web Site Factory read more
TIP: How To Generate a Fully Qualified URL in ASP.NET (E.g., http://www.yourserver.com/folder/file.aspx) read more
Why not Classic (Legacy) ASP? read more
RELEASED ASP.NET MVC 2 Preview 2 read more
Quick Reference Guide for Telerik Support read more
Western European ReMix Tour 2009 read more
Ever wonder how .NET finds the assemblies that you reference ? read more
NDC 2009 - Get the code! read more
How do ASP.NET Application_ Events Work read more
Some quick updates read more
Top
 
 
 

Discussion


Subject Author Date
placeholder Tell me more about the big picture William Barnum 2/27/2010 1:55 PM
Zip file Raphael Shekwolo 2/27/2010 3:04 PM
placeholder RE: Zip file Sonu Kapoor 2/27/2010 5:47 PM
source code for demo app Dylan Thomas 2/27/2010 5:26 PM
placeholder RE: source code for demo app Sonu Kapoor 2/27/2010 5:47 PM
RE: RE: source code for demo app Dylan Thomas 2/28/2010 10:29 PM
placeholder Source Code Benjamin van der Veen 2/27/2010 5:48 PM
Congratulations Jack Paterson 3/1/2010 7:42 AM

Please login to rate or to leave a comment.