Want to follow this site? Here's the RSS feed.

Fundamentals of Themelia - Basic HTTP Routing

Wednesday, June 4, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

Themelia (Th-meh-LEE-uh; Greek for foundations) is the foundation for every web site and web application that I have built in the past few years.  While there are many aspects to Themelia, just about everything in Themelia exists to support its HTTP routing system which provides a completely new paradigm for all ASP.NET development.

In this entry, I'm going to give a basic introduction to Themelia 2.0 HTTP routing.  Be aware, however, that the work basic doesn't even do this justice.  This is the tip of the ice berg of basic.

Simple HTTP Handler Registration

In any of my web applications, I'll have anywhere from 5 to 30 HTTP handlers. They are small, powerful, and easy to build. When I don't need a web form, I don't need the its associated overhead either. If you need a simple authentication endpoint, a place for your Facebook or PayPal application to call back, or just need a simple non-WCF HTTP endpoint to receive and process data, then you seriously need to look into using an HTTP handler. Below is a sample HTTP handler.

namespace Sample
{
    public class AuthenticationHttpHandler : System.Web.IHttpHandler
    {
        //- @IsReusable -//
        public Boolean IsReusable
        {
            get { return true; }
        }
        //- @ProcessRequest -//
        public void ProcessRequest(System.Web.HttpContext context)
        {
            context.Response.Write("Hello");
        }
    }
}

However, one of the problems with HTTP handlers is that they are simple classes that implement the System.Web.IHttpHandler interface and, therefore, have no immediate access endpoint. ASP.NET does, however, provide a file extension called ASHX which it registers with IIS to provide HTTP handlers an endpoint. Personally, I've always found that to be an extremely sloppy technique as it requires you to have an ASHX file for every HTTP handler. That's not going to happen. Furthermore, uhh... file extensions suck!

A much more effective mechanism for creating and HTTP handler endpoint is to register it in your web.config file with a specific path (i.e. /MyHandler.aspx or /MyHandler/). You get the same result without having to create a completely superfluous file. However, to make the latter endpoint accessible in IIS6, you need to setup a wildcard mapping to ASP.NET. An alternative and more powerful way of doing this is by using a very carefully designed HTTP module.

The major downside here is that HTTP handler registration is different between NetFXHarmonics DevServer/IIS6 and II7. You have to register each handler once in the <system.web> section for DevServer and IIS6 and once again in the <system.webServer> section for IIS7.  Furthermore, if you are using the IIS7 integrated pipeline, you have to remove the IIS6 registrations or an exception will be thrown.  While I find this duplicate registration to be 100x better than actually creating a ton of ASHX files that do little more than cluttering the world, it's still too much work.

Therefore, Themelia allows you to have one central registration point for all HTTP handlers. It does this by providing a HTTP module which handles all web site traffic.  Whereas HTTP handlers handle a specific HTTP request,  HTTP modules handle everything everywhere.  To use Themelia 2.0, you just have to register it's RoutingModule as an HTTP module in IIS6 or IIS7. Now, instead of registering 30+ handlers twice, you are registering your specific 30+ handlers in Themelia's centralized location. In the following example you can see the basic mechanism for registering Themelia with your ASP.NET application.

<system.web>
  <httpModules>
    <add name="Routing" type="Themelia.Web.Routing.RoutingModule, Themelia.Web" />
  </httpModules>
</system.web>
<system.webServer>
  <modules>
    <add name="Routing" preCondition="" type="Themelia.Web.Routing.RoutingModule, Themelia.Web" />
  </modules>
</system.webServer>

With this simple module registration, you application is running on Themelia 2.0.  However, it's not really doing much yet.  Configuration is done in web.config and may go from extremely simple to rather complex (not to be confused with "complicated"; complex just means "multiple parts").  In further documentation, the configuration will become more and more complex providing for more power and flexibility.

However, as with any custom .NET configuration, you must first register the configuration section at the top of your web.config file as the following snippet shows (or to register for all ASP.NET application on your server use your machine.config instead -- %systemroot%\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config):

<configuration>
  <configSections>
    <section name="themelia.web" type="Themelia.Web.Configuration.ThemeliaWebConfigurationSection, Themelia.Web"/>
  </configSections>
</configuration>

In terms of actually configuring Themelia, one of the easiest configurations is seen in the below example taken from Themelia's "Basic" sample.  This sample does a simple registration of the "Authentication" HTTP handler.

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="Authentication" text="/authenticate/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

The matchType may have one of eight possible types: "startsWith, "endsWith", "contains", "pathStartsWith", "pathContains", "pathEquals", "webDomainPathEquals", and "webDomainPathStartsWith".  The first three match against the entire URL while the next two only match against the path of the URL after the prototcol, address, and port.  The last one requires knowledge of web domains and is discussed in the web domains documentation.  As an example, the first three will match against "http://127.0.0.1/data/processing.svc" while "pathStartsWith" and "pathContains" will only match against  "/data/processing.svc".

Handler Selection

When a request comes in, Themelia uses the information provided it to select the proper action to take.  When dealing with handler registration, there is a handler selection process which chooses the correct handler.  However, more than one handler may satisfy a particular request.  For example, the URL path of /user/download/ would match both a matchType of "contains" and "endsWith".  If the two handler registrations send the request to two different places, there's a big result difference.  Even then, if two handler registrations are matching with "contains", but one is matching against /user/ and the other is matching against /user/download/, we again have a situation to deal with.

Fortunately, the solution to the problem is obvious.  Themelia internally internally sorts all handlers by matchType in logical order of specificity (the measure of how specific something is) and then by text length.  Here's the specificity order:

  • pathEquals
  • endsWith
  • webDomainPathStartsWith
  • webDomainPathEquals
  • pathStartsWith
  • startsWith
  • pathContains
  • contains

Regular Expression Shorthand

In addition to the above matching keywords, you may also use a form of regular expression shorthand.  For example, you can begin your handler text with "^" and it will automatically change your match type to "startsWith".  In the same way, ending your handler text with "$" will automatically change it to "endsWith".  For example, "/authenticate/$" converts the matchType to "endsWith".  Here's a complete list:

  • Beginning with "wdp^" and ending with "$" means "webDomainPathEquals"
  • Beginning with "p^" and ending with "$" means "pathEquals"
  • Beginning with "^" and ending with "$" means "equals"
  • Beginning with "wdp^" by itself means "webDomainPathStartsWith"
  • Beginning with "p^" by itself means "pathStartsWith"
  • Beginning with "^" by itself means means "startsWith"
  • Ending with "$" by itself means means "endsWith"

These override anything that you put in the matchType attribute.

HTTP Handler Aliasing

Here's a simple quiz to see if you are paying attention or to see how well you know ASP.NET: What HTTP handler will Themelia match for /authenticate/? Go ahead, move your eyes up a few paragraphs and check it out.  The answer is simple: you have no idea because "Authentication" is the name of the handler, not a valid .NET type. In this example, Themelia is utilizing an, optional, feature called an handler factory to obtain the HTTP handler.

A HTTP handler factory is a class that inherits from the Themelia.Web.Routing.HandlerFactoryBase class and implements the following method signature, which accepts a handler alias as a parameter and returns an HTTP handler instance:

IHttpHandler CreateHttpHandler(String text)

Using a handler factory, you can create aliases to allow yourself and others easier access to HTTP handlers. In this case "Sample.AuthenticationHttpHandler, Sample" was shortened to the alias "Authentication" with the following Sample.SampleHandlerFactory class.

namespace Sample
{
    public class SampleHandlerFactory : Themelia.Web.Routing.HandlerFactoryBase
    {
        //- @CreateHttpHandler -//
        public override IHttpHandler CreateHttpHandler(String text)
        {
            switch (text)
            {
                case "auth":
                case "authentication":
                    return new AuthenticationHttpHandler();
            }
            //+
            return null;
        }
    }
}

As you can see, because C# allows contentless fall-through in switch statements, you can very easily create more than one alias for an HTTP handler. Therefore, the following handler registration would work too:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="Auth" text="/authenticate/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

You may have any number of handler factories, perhaps one for each logical grouping of handlers or to make the handler names more streamlined. For example, if you are creating a series of callback endpoints for various social networking services, maybe you want to create a HTTP handler factory which shortens the names of the handlers involved and suffixes them with "Callback" like "FacebookCallback" and "PayPalCallback".

Handler factories, like all Themelia factories, are registered in the web.config under the <factories> collection. The following configuration snippet demonstrates this:

<themelia.web>
  <webDomains>
    <add>
      <factories>
        <add type="Sample.Web.HandlerFactory, Sample.Web" />
      </factories>
    </add>
  </webDomains>
</themelia.web>

It's important to realize that http handler factories are completely optional. They just make future development easier to think about. If you have HTTP handlers you haven't yet put into a http handler factory, you can still register the handler in the name attribute using a fully qualified .NET name like the following example.  However, be aware that if you do not use a handler factory, obviously Themelia will be dynamically creating that type on the fly. So, it's strongly encourages that you always use an handler factory even if you have only one handler.

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="Sample.Web.AuthenticateHandler, Sample.Web" text="/authenticate/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

Temporarily Disabling Themelia

You may also disable all Themelia routing by setting the disableRouting configuration attribute to true as the following example shows:

<themelia.web disableRouting="true">
</themelia.web>

Web Domains

There's another concept in Themelia 2.0, namely a WebDomain, but it's a topic beyond the scope of this discusses.  However, it's important to know that a web domain is essentially a web AppDomain.  Whereas an AppDomain separates segments of an application, a web domain separates the segments of a web site.  For a single-web domain Themelia application, only the "root" (or default) web domain is used.  That's why you see the <webDomains><add></add></webDomain> stuff in the examples.

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.5 License.

Built on Themelia Pro 2.0

Mini-icons are part of the Silk Icons set of icons at famfamfam.com