The conception, birth, and first steps of an application named Charlie

Subscribe: Atom or RSS

Charlie has Cool URLs - Part 1

by Alister Jones (SomeNewKid)

An ongoing joke in Beavis and Butthead is that, to the boys, everything is either cool or it sucks. There is no inbetween. It is cool. Or it sucks.

I have the same black-and-white approach to URLs. A given URL is either cool, or it sucks. Apple has cool URLs:

www.apple.com/ipod

The URLs for Creative suck:

www.creative.com/products/product.asp?category=213&subcategory=214&product=11519

Charlie has cool URLs, if I do say so myself. It has been a four-step process to give Charlie cool URLs, and I’ll dedicate one weblog entry to each step.

The first step to giving Charlie cool URLs was to implement URL rewriting. This is the process by which the incoming cool URL (such as /ipod) is rewritten to the “real” file that handles the request (which may be /products.aspx?id=10). A common technique is described in URL Rewriting in ASP.NET, whereby all incoming page requests are rewritten to one of a handful of actual .aspx pages. The code looks like this:

String coolUrl = HttpContext.Current.Request.Path;
String targetUrl = UrlRewriter.GetHandler(coolUrl);
HttpContext.Current.RewritePath(targetUrl);

In such a URL rewriting system, the developer maintains a handful of .aspx pages, such as ShowProduct.aspx, ShowCategory.aspx, and ShowPerson.aspx. The details of what to show are passed in via QueryString variables. So, the above targetUrl string might look something like this:

ShowProduct.aspx?category=16&product=128&view=details

For most web applications, the process of sheperding all incoming requests to one of a handful of .aspx pages is all that is needed. But Charlie has a special requirement that means this common approach isn’t quite right for my project. The special requirement is that Charlie must host multiple websites from a single installation, and each website must be able to generates pages that have nothing at all in common with pages from another website. For this requirement to be met, the pages must be completely dynamic in their creation. With an .aspx page, some of it will be static (whatever is already on the .aspx page) and some of it will be dynamic. So having a handful of .aspx pages would not meet this requirement of Charlie. So, what are the alternatives? Well, there are two least two.

The first option available for a web application to dynamically create the entire page is to have a single .aspx page with nothing other than a Page directive that points to a page-building code-behind file:

<%@ Page language="C#" AutoEventWireup="false" Inherits="PageBuilder" %>

The page has nothing on it, so its entire content must be generated dynamically. If we place this .aspx in the root folder of the web application, the above URL rewriting code would be adjusted to look something like the following:

String coolUrl = HttpContext.Current.Request.Path;
String querystring = UrlRewriter.GetQueryString(coolUrl);
HttpContext.Current.RewritePath("~/default.aspx?" + querystring);

The QueryString might look something like this:

page=36&template=blue&category=16&product=128&view=details

So, whereas the technique from the article means the developer maintains a handful of separate .aspx pages, this first alternative means the developer has just a single .aspx that, frankly, does nothing—the work is done by the page-building code-behind.

The second option available for a web application to dynamically create the entire page is a variation of the first option just described. Whereas the first option used a single .aspx and a single code-behind, the second option does away with the .aspx page altogether. The .aspx page does nothing except point to the code-behind, yet there’s another way we can point to the code-behind. Namely, we can use the Web.config file to direct all .aspx page requests to that code-behind. Here is the relevant section of the Web.config file:

<httpHandlers>
    <add verb="*" 
         path="*.aspx" 
         type="PageBuilder, AssemblyName"/>
</httpHandlers>

What this means is that any request with an .aspx extension will be handled by the PageBuilder class. There is no need for any .aspx page to exist, which means that the above URL rewriting code can be modified like this:

String coolUrl = HttpContext.Current.Request.Path;
String targetUrl = UrlRewriter.GetHandler(coolUrl);
HttpContext.Current.RewritePath(targetUrl);

Yes, this is the same code as that shown first. However, previously the targetUrl had to point to an existing .aspx page. This time, the targetUrl does not need to point to any real .aspx page, and could look like this:

show-a-porsche-with-a-black-background.aspx?otherwise=ferrari

Using a virtually-empty .aspx file and using a Web.config entry both do the same thing: point to the code-behind that dynamically creates the entire page. While I could have used either option, I went with the second option of using the Web.config to point to the code-behind. First, I didn’t fancy having a do-nothing .aspx page. Second, and for reasons I’ll come to in my next weblog entry, Charlie would end up needing quite a number of these do-nothing pages. The Web.config option makes the do-nothing pages unnecessary, so that is the approach I took.

by Alister Jones | Next up: Charlie has Cool URLs - Part 2

0 comments

----