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

Fundamentals of Themelia - PassThrough Handling

Tuesday, June 24, 2008

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

One very important thing that you have be me aware of is Themelia's pass through handler.  This is a special handler isn't really a handler at all, but is rather used to signal to Themelia not to set any HTTP handler at all, but to pass the request back to IIS for it to decide the fate of the route.  Registering a path to this handler is the same as registering a path to any other handler as the following example will demonstrate:

<themelia.web>
  <webDomains defaultTarget="/Page_/Photo/Gallery.aspx">
    <add>
      <handlers>
        <add matchType="pathStartsWith" name="PassThrough" text="/document/pdf/" />
        <add matchType="pathStartsWith" name="PassThrough" text="/home/picture/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

In this situation, when anyone accesses http://www.yourdomain.com/document/pdf/ or http://www.yourdomain.com/home/picture/, Themelia won't set any handler at all, but will just let IIS handle it, which would normally means that you probably have physical folders or IIS virtual folders set for those paths.

ForceUse

No, this has nothing to do with Jedi mind tricks.  The ForceUse pass through handler technique is the most powerful way to over ride Themelia routing.  While setting a simple pattern matching as seen in the above example may be enough for most of your needs, using the ForceUse technique is much more powerful.  This involves simply setting the PassThroughHttpHandler.ForceUse property to true.

Where has registering the pass through handler will continue the Themelia pipeline allowing the mid and post processors to reset the handler, using the ForceUse property will stop the Themelia pipeline immediately after preprocessing ends.  Therefore, there will be absolutely no chance of the route being changed.  This logically means that the handlers registered as pass through in the above example, could actually be overridden to use a different handler.  For example, Themelia internally sets robots.txt, favicon.ico and WebResource.axd to use the "passthrough" handler.  However, if you want to have custom logic with a slick handler for any of these, then go ahead and create a mid processor or, more likely, as a post processor to override this.

Now, while while you may alter those three registrations as you see fit, Themelia has two reserved folders that you are absolutely no allowed to alter.  These are the /Resource_/ and /Service_/ folders.  These two folders will always use the pass through handler as they, in an internal preprocessor, have set PassThroughHttpHandler.ForceUse to true.  These folders are reserved to encourage a level of structure in web sites.  It's as bad of an idea to put files in root of your website as it in to put files in on the root of your system hard drive.  This is what folders are for.

Therefore, the /Service_/ folder could contain WCF services (i.e. svc files) and the /Resource_/ folder is where you could store images, styles, XAML documents, scripts, and other client-side resources. In fact, any one of my web sites have the following sections:

/Resource_/Code/ 
/Resource_/Image/ 
/Resource_/Lib/ 
/Resource_/Media/ 
/Resource_/Style/ 
/Resource_/Xaml/

If you don't want to use these folders, you don't have to.  It's not like Themelia creates the folders for you.  They are simply reserves as pass through.  If you want to have your own Style, Image, or Xaml folders, just setup a simple pass through handler or, to really make a point, create a preprocessor that does custom pattern matching (the Themelia.Web.Http class will help), and set the PassThroughHttpHandler.ForceUse property to true.

Initialization Parameters (Advanced)

Now, if you would like to do use the more hardcore method of the ForceUse, but don't want to create your own preprocessors, you have another option in Themelia's PassThroughPreProcessor.  This preprocessor does nothing more take your requests for patterns to be set to ForceUse.  So, if you have a bunch of areas you want to force to be passed to IIS, skipping the bulk of the Themelia pipeline, you may use this preprocessor.

To use this preprocessor, you don't actually do anything in code.  You actually use Themelia initialization parameters.  This essentially means that Themelia allows you to do inline pass through registrations.  Here's an example:

<themelia.web>
  <webDomains defaultTarget="/Page_/Photo/Gallery.aspx">
    <add>
      <preProcessors>
        <add type="PassThrough">
          <parameters>
            <add name="pdf" value="pathStartsWith,/document/pdf/" />
            <add name="photos" value="pathStartsWith,/home/picture/" />
          </parameters>
        </add>
      </preProcessors>
    </add>
  </webDomains>
</themelia.web>

With initialization parameters, you can initialize certain (or custom) aspects of your system in configuration.  In this case, you are setting up a hardcore directive forcing two patterns to be passed to IIS.

A pass through initialization parameter consists of two parts: a name of your own choosing and a value consisting of the match type, a comma, and the path to match.  If no match type is specified as in the following example, "contains" is used by default:

<themelia.web>
  <webDomains defaultTarget="/Page_/Sales/Report.aspx">
    <add>
      <preProcessors>
        <add type="PassThrough">
          <parameters>
            <add name="excel" value="/document/xls/" />
          </parameters>
        </add>
      </preProcessors>
    </add>
  </webDomains>
</themelia.web>

Note that the types here are the same as used with HTTP handler registration: "startsWith, "endsWith", "contains", "pathStartsWith", "pathContains", "pathEquals", "webDomainPathEquals", and "webDomainPathStartsWith".  The example above with this explanation should lead you down the right path.

If you are activating the PassThroughPreProcessor via a Themelia web component, then just set the initialization parameter using the following programmatic syntax:

public class SecurityComponent : Themelia.Web.Routing.ComponentBase
{
    //- @Register -//
    public override void Register(PreProcessorDataList preProcessorDataList, ProcessorFactoryDataList processorFactoryDataList, HandlerFactoryDataList handlerFactoryDataList, InjectionProcessorDataList injectionProcessorDataList, MidProcessorDataList midProcessorDataList, FallThroughProcessorDataList fallThroughProcessorDataList, PostProcessorDataList postProcessorDataList, PostStateProcessorDataList postStateProcessorDataList, ErrorProcessorDataList errorProcessorDataList)
    {
        preProcessorDataList.Add(new PreProcessorData
        {
            ProcessorType = "PassThrough",
            ParameterArray = new Object[] { "pathStartsWith,/document/pdf/", "pathStartsWith,/home/picture/" }
        });
    }
}

Links

Fundamentals of Themelia - FallThrough Processors

Friday, June 20, 2008

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

After mid processors have executed, Themelia runs an HTTP handler selection algorithm to find the best match for the specific request. If there is no handler match, a fall through processor, if set, catches the route and handles it directly.  In a way, you could think of this as being similar to the "default" statement in a C# switch block.  If no fall through processor is provided or if all registered fall through processors return null, Themelia defaults to passing the request back to IIS to let if figure out the route.

In the basic Themelia web site sample, there is an authentication HTTP handler which handles /authentication/ and the sample web site also rewrites /login/, /logout/, and /problem/. Now, while we know exactly what happens with these explicitly stated routes, it tells us absolutely nothing about what to do about a request to /contact/. In this scenario, if no fall through processor were set, it would be up to IIS to handle the request. If IIS doesn't have anything for it to do (i.e. there is no /contract/ folder or virtual folder), the end user may receive a 404 error. Actually, this may be exactly what you want sometimes.

A fall through processor is made by creating a class that inherits from Themelia.Web.Routing.FallThroughProcessorBase. This is an abstract class that requires you to implement the following signature. The signature should look very familiar to ASP.NET veterans as it's the same signature used in HTTP handler factories as that's functionally what a fall through processor is.

System.Web.IHttpHandler GetHandler(System.Web.HttpContext context, String requestType, String virtualPath, String path, params Object[] parameterArray);

Fall through processors may be registered in web.config like this:

<themelia.web>
  <webDomains>
    <add>
      <fallThroughProcessors>
        <add type="Themelia.Web.Routing.ForbiddenFallThroughProcessor, Themelia.Web" />
      </fallThroughProcessors>
    </add>
  </webDomains>
</themelia.web>

There are a few fall through processors provided with Themelia. These are WebFormFallThroughProcessor, ForbiddenFallThroughProcessor, UrlRewriteFallThroughProcessor, PassThroughFallThroughProcessor, and RedirectFallThroughProcessor. If one of these is set, all routes not handled by the Themelia handle selection algorithm will be caught by one of these fall through processors or by a custom one of your own design.  Let's run through each of these to go over what value each provides.

The first of the fall through processor I would like to cover here, WebFormFallThroughProcessor, has a fairly obvious purpose: it catches routes and turns them into WebForms. You would use this fall through processor in a scenario where you have major sections (see Themelia Web Domains) of your web site that have nothing but web forms.

The next fall through processor that should be discussed is the ForbiddenFallThroughProcessor. This one essentially ties down all loose ends in your web site.  The idea is that, if the HTTP selection algorithm doesn't find a good match for your route, ForbiddenFallThroughProcessor will set the active handler to BlockedHttpHandler, which does exactly what the name suggests: blocks the request. In other words, anything not explicitly stated in Themelia, will be denied. For example, in the sample Themelia web site, /contact/ would show you a big sign reading "This part of the system is not publicly accessible."

The last three fall through processors are specialized processors that have special meaning in their own context.  In fact, each is described in detail in their respective sections: PageAlias, PassThrough, and Redirect.

Of course, you can make your own fall through processor as well. Perhaps you want to make something that does nothing more than sends you to you a more exciting 404 page than what people are traditionally used to. It can be anything. If you ever wanted a "catch all" for your web sites, you now have it.

One major example of a fall through processor is in Minima 3.1, which uses its own BlogFallThroughProcessor to handle all unhandled traffic. This is incredibly important since Minima lives off this ability. The BlogFallThroughProcessor will handle every request from /2008/05 to /label/aspnet to /2008/05/Minima-30-Released. Therefore, Minima obviously requires full control over the entire URL to know what information to display.

Fall through processors are also compatible with Themelia processor factories, so feel free to create aliases for each of your fall through processors.  The above example, using Themelia's internal processor factory allows you to shorten the configuration to:

<themelia.web>
  <webDomains>
    <add>
      <fallThroughProcessors>
        <add type="ForbiddenFallThroughProcessor" />
      </fallThroughProcessors>
    </add>
  </webDomains>
</themelia.web>

Fall Through Processor Initialization Parameters

Various features in Themelia have what are called initialization parameters. The ForbiddenFallThroughProcessor, for example, is aware of these parameters and allow you to provide the processor with information it may use at a later time.

Normally, when we set the ForbiddenFallThroughProcessor all requests which hit this processor simply end there with no output.  However, by using an initialization parameter we can have the processor display text instead.  Here's an example of using an initialization parameter with a fall through processor:

<themelia.web>
  <webDomains>
    <add>
      <fallThroughProcessors>
        <add type="ForbiddenFallThroughProcessor">
          <parameters>
            <add name="text" value="You do not have access to view this page." />
          </parameters>
        </add>
      </fallThroughProcessors>
    </add>
  </webDomains>
</themelia.web>

Initialization parameters follow the "name/value" pattern, but the name portion is often unused.  In this case, name is ignored and the value is what's used to display the message.

If you are installing the fall through processor via a Themelia web component, then just set the initialization parameter using the following programmatic syntax:

//- @Register -//
public override void Register(FactoryDataList factoryDataList, ProcessorDataList processorDataList, HandlerDataList handlerDataList, AliasDataList aliasDataList, RedirectDataList redirectDataList)
{
    factoryDataList.Add(FactoryData.Create("Sample.Web.Routing.ProcessorFactory, Sample.Web"));
}

As a closing note, it's important to note that a handler might not even be set after all fall through processors complete. Of course that's not the case with WebFormFallThroughProcessor and ForbiddenFallThroughProcessor, but if you were to write your own custom fall through processors, you could return null to have the request handled my IIS directly.  In fact, if no fall through processor returns any handler, Themelia sets the route to use its magical PassThrough handler, which simply signals Themelia not to handle the request, but to pass it to IIS.

Links

Fundamentals of Themelia - Pipeline Processors (Pre, Mid, Post, and PostState)

Wednesday, June 18, 2008

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

Themelia has various processors which handle allow for custom logic at various points in the Themelia pipeline.  These processors include pre processors, mid processors, post processors, and post state processors and since they work directly on the pipeline they are referred to as the pipeline processors. I'll just run through them, explaining what they are, how to build and use them and also give a few examples.

Pre Processors

PreProcessors allow the developer to do any custom logic that is required to run after HTTP modules, but before any other part of the system. These also run before any HTTP handlers as well as before any other feature of Themelia. You can use preprocessors for many things, but they were originally designed to setup the environment for the HTTP handlers.

For instance, perhaps you want to parse the query string, set a context item, detect for a certain browser or even want to pull something directly from the HTTP POST or modify viewstate. This is a good place to do those things. All your settings will be in place before your HTTP handlers are called and, therefore, before web forms even think about loading. So, this is your chance, if you are using webforms, to do processing to control data even before Init, PreInit, and even the web form constructor!

One example of a preprocessor is in Minima called MinimaPreProcessor. Minima uses this feature to read the BlogGuid from configuration so that the other HTTP handlers will have it available. Themelia itself also uses preprocessors, but these are used to support features that will be discussed in a future post. For now, however, feel free to look at the Themelia source code to see these examples of preprocessors.

Another example of a PreProcessor is in Themelia itself.  Something has to read the Themelia configuration, right?  Internally, Themelia uses the ConfigurationPreProcessor to do all configuration processing so that the rest of the system will have all the information it needs.  There are

A preprocessor is made by creating a class that inherits from Themelia.Web.Routing.PreProcessorBase. This is an abstract class that requires you to implement the following signature. However, the params array in this signature is used for Themelia internal use, for Themelia web components as well as for processor initialization, the latter being topics for future discussions.

void OnPreProcessorExecute(System.Web.HttpContext context, params Object[] parameterArray)

Though this isn't the place for a full discussion of it, pre-processing may be the last thing that runs in certain cases.  This possibility comes from a Themelia feature called PassThrough.  For now, be aware, that there's more going on than the execution of the various processors written about here.

Mid Processors

Mid processors are a bit similar to preprocessors in that their sole purpose is to do any form of processing that you need. However, these are done after preprocessors have run, handler factories have loaded, and after injection processors (discussed elsewhere) have injected their handlers, but just prior to HTTP handler selection. This gives you an opportunity to use the information already provided to you by the other Themelia features to possibly alter the routing process in some way.

Probably the most obvious use of a mid processor is in detecting to see if the information already provided gives enough information to skip the entire HTTP handler selection process, which would otherwise happen just after all mid processors run. For example, based on certain security information provided to you in a custom preprocessor, you may already know you need to use a specific injected HTTP handler.

A mid processor is made by creating a class that inherits from Themelia.Web.Routing.MidProcessorBase. This is an abstract class that requires you to implement the following signature:

IHttpHandler OnMidProcessorExecute(HttpContext context, params Object[] parameterArray)

By returning an HTTP handler, the HTTP selection process is skipped.  By returning null, routing continues as usual.

Fall Through Processors (not a Pipeline Processor)

Though, there's more to it than this, after mid processors have executed, Themelia runs a HTTP handler selection algorithm to find the best match for the specific request.  If there is no handler match, a fall through processor, if set, catches the route and handles it itself. If no fall through processor, which isn't actually a pipeline processor) is provided or if all registered fall through processors return null, Themelia defaults to passing the request back to IIS to let if figure out the route.

However, due to the fact that they are so different than these other types of processors, they are discusses in a separate place.  For the time being though, just know that they run after mid-processors, but before post-processors.  After you see a few examples of this and read some more docs, this should all make more sense.

Post Processors

Post processors are a bit similar to pre processors and mid processors. However, these are the very last processors to run in the route activator.  This means that run after fall through processors (discussed elsewhere), giving you one last chance to override something a previous processor or even the fall through processor may have done.  In fact, this is where you could set a handler if the fall through processor didn't.  If you want to switch to a different HTTP handler based on some custom logic, this is your time to do it.

A post processor is made by creating a class that inherits from Themelia.Web.Routing.PostProcessorBase. This is an abstract class that requires you to implement the following signature:

IHttpHandler OnPostProcessorExecute(HttpContext context, IHttpHandler activeHttpHandler, params Object[] parameterArray)

As you can see from this signature, the currently active HTTP handler comes in as a parameter.  This means you may use the HTTP handler at this point for some reason.  Perhaps you would like to set a few properties on the handler or even call a handler method.

By returning an HTTP handler, you are effectively override all previous processors. By returning null, the existing HTTP handler remains and routing continues as usual.

One final note that shouldn't be overlooked is that at any point in the Themelia pipeline, you can send a signal to skip post-processing.  This should obviously be used with extreme caution, but if you do want to skips this processors, you may call the FlowControl.SkipPostProcessing() method and post processing won't execute.

PostStateProcessors

Post state processors are similar to preprocessors in that they exist simply to work with information.  A preprocessor may load some information to prepare the pipeline whereas a post state processor may do some last-chance processing on information from the pipeline.  A post state processor, as its name suggests, runs after ASP.NET state has been initialized and therefore has full access to session information.  So, if you need to configure some information in session, this is your place to do it.  However, remember that by the time a post state processor runs, your HTTP handler has already been set and you are not allowed to change it.

This type of processor is actually the only processor that has direct access to session information.  However, that doesn't mean that you can't set session in other processors.  How's that possible if you don't have direct access to session?  Themelia internally has a post state processor called SessionPostStateProcessor which runs before any custom post state processors.  This post state processor has a single object in it called Data of StringObjectMap.  Any information set in this single, will be copied to session before custom post state processors runs.

As an example, the following preprocessor, while it doesn't have access to session, registers a string to session via the SessionPostStateProcessor:

public class AuthenticationPreProcessor : PreProcessorBase
{
    public override void OnPreHttpHandlerExecute(HttpContext context, params Object[] parameterArray)
    {
        SessionPostStateProcessor.Data["UserName"] = "John Doe";
    }
}

When using this or the even items context, session data, or cache data, it's generally a good idea to scope your information using the scope operator.  The above example is rather poorly written because the session item name "UserName" is rather general and may be overwritten by someone else.  Therefore, something like the following is recommend:

public class AuthenticationPreProcessor : PreProcessorBase
{
    //- @Info -//
    public class Info
    {
        public const string Scope = "Authentication";
    }

    //- @OnPreHttpHandlerExecute -//
    public override void OnPreHttpHandlerExecute(HttpContext context, params Object[] parameterArray)
    {
        SessionPostStateProcessor.Data[Info.Scope + "::UserName"] = "John Doe";
    }
}

Themelia inherently understands item scope and uses it at many places internally.  To access the above information after it's in session, you may use the following:

Themelia.Web.HttpData.GetScopedSessionItem<String>(AuthenticationPreProcessor.Info.Scope, "UserName");

A post state processor is made by creating a class that inherits from Themelia.Web.Routing.PostStateProcessorBase. This is an abstract class that requires you to implement the following signature:

void OnPostStateProcessorExecute(HttpContext context, params Object[] parameterArray)

This signature should look familiar as it's basically the same signature as a preprocessors.

These four types of processors give you a wide variety of control over the flow of your routing.  Each of these concepts give you a measure of control over the entire HTTP pipeline.  However, these are intended to be understood by themselves.  Without, for example, a full understanding of fall through processors, the flow of the Themelia pipeline is incomplete.

Registration

Registering any type of processor in Themelia is as simple as providing the site's web.config file with the processor type:

<themelia.web>
  <webDomains>
    <add>
      <processors>
        <add type="Sample.Web.Routing.PreProcessor, Sample.Web" />
        <add type="Sample.Web.Routing.MidProcessor, Sample.Web" />
        <add type="Sample.Web.Routing.PostProcessor, Sample.Web" />
        <add type="Sample.Web.Routing.PostStateProcessor, Sample.Web" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

Processor Factories

No one likes to type long fully qualified types.  Therefore, Themelia provides various categories of factories to help register various pieces.  For factories, we have processor factories.  By using a processor factory we can turn the above configuration into the following:

<themelia.web>
  <webDomains>
    <add>
      <processors>
        <add type="PreProcessor" />
        <add type="MidProcessor" />
        <add type="PostProcessor" />
        <add type="PostStateProcessor" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

For more information on processor factories, see the processor factory documentation.

Links

Fundamentals of Themelia - Page Aliasing

Thursday, June 12, 2008

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

One of the core HTTP handlers that ship with Themelia is the page alias handler, which allows users to access SEO-friendly (search engine optimization) paths. For example, a user would see /login/ instead of something disgusting like /Login.aspx.  Themelia page aliasing is similar to a concept called URL rewriting mechanism (though with major differences discussed further down).

Registering a Themelia page alias is done in two steps: first, register the path to a PageAlias handler and, second, register the alias with the alias configuration collection in Themelia, linking them together with a key.  The key is the key attribute on the alias and the text attribute or, if present, the referenceKey attribute on the handler declaration.  The key is very important.  While, as is explained in basic HTTP routing, handlers are loaded based on rules of specificity, aliases are loaded via the key.

Below is an example configuration snippet; notice that the HTTP handler name has an alias of "PageAlias", meaning it's registered in Themelia's internal HTTP handler factory.

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="PageAlias" text="/login/" />
        <add matchType="contains" name="PageAlias" text="/logout/" />
        <add matchType="contains" name="PageAlias" text="/contact/" referenceKey="c" />
      </handlers>
      <aliases>
        <!--login-->
        <add key="/login/" target="/Sequence_/Security.aspx" />
        <add key="/logout/" target="/Sequence_/Security.aspx" />
        <!--suggestion-->
        <add key="c" target="/Sequence_/Contact.aspx" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

Here you can see that the login and logout aliases are keyed to their respective handlers via the text while the contact alias is keyed via the referenceKey.  The referenceKey attribute is completely optional.  You may use it all of the time, some of the time, or none of the time.  However, as a common sense practice, you want to be congruent (not "consistent"; consistency says "I always drive 45mph.  Doesn't matter if it's a school zone or the interstate" while congruency says "I will drive according to the common sense underlying principles of my current situation: 18mph is a school zone and 65mph on the interstate.)  Therefore, you may want to either always use the referenceKey, or only use it when the handler text gets beyond 20 characters.

Now, by way of commentary, in the above example, when a user accesses either /login/ or /logout/, he or she will actually be sent /Page_/Security/Login.aspx under the covers. But what would differentiate between the two in /Security/Login.aspx? The answer is obvious: just check to see which the user is accessing. In the following example, you can also see how Themelia provides you with simple helper mechanisms:

public partial class Login : Sample.ThemeliaPage
{
    //- @Setting -//
    public class Setting
    {
        public const string IsAuthenticated = "IsAuthenticated";
    }

    //+
    //- #OnInit -//
    protected override void OnInit(EventArgs e)
    {
        if (Themelia.Web.Http.UrlPartArray.Contains("logout"))
        {
            Themelia.Web.HttpData.SetSessionItem<Boolean>(Setting.IsAuthenticated, false);
        }
        //+ check security
        if (Themelia.Web.HttpData.GetSessionItem<Boolean>(Setting.IsAuthenticated))
        {
            Themelia.Web.Http.Redirect("/");
        }
        //+
        base.OnInit(e);
    }
}

Themelia provides you with a class called Themelia.Web.Http which contains various members to aide development, including a UrlPartArray member which is an array containing the different parts of the virtual path accessed. In this case UrlPartArray[0] would have "Login" or "Logout". C# 3.0 LINQ is being used to check for this. Themelia.Web.HttpData also includes a number of generic methods to help you access namespaced (scoped) session, cache, and context items in a strongly typed manner.

Regular Expression Match and Variable Capture

One important thing you can do with page aliasing is use a regular expression to create patterns of matching.  You use a typical regular expression matching pattern with a $x replacement.

You need to be careful using this, however, as over use will create sloppiness.  You don't want to map all of one logical path to all of a physical path as this brings you right back to the tight coupling between the logical and physical structure.  For example, it's a very bad idea to use /product/([a-z]+)/$ to map to "/Page_/Product/$1.aspx.  That does absolutely nothing more than reconnect your logical and physical structures.  The point is to separate them, not to create a pattern that connects them just as tight as when you started.

Therefore, in order to create a clean, loosely-coupled structure, regular expressions should be used primarily for variable capture.  Here's an example:

<themelia.web>
  <webDomains>
    <add defaultPage="/Page_/Security/Home.aspx">
      <handlers>
        <add matchType="contains" name="PageAlias" text="/problem/([\-a-z0-9]+)" referenceKey="problem" />
      </handlers>
      <aliases>
        <add key="problem" target="/Page_/Quality/Problem.aspx?guid=$1" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

Notice a few thing about this. First, the regular expression in the handler text copies a variable to the $ variable in the alias.  If there were more than one capture, the number in the alias increments (i.e. $1, $2, $3, etc...)  Second, while you may still use the handler text as the key, regular expressions can be long and complex, therefore, it's probably a good idea to use the referenceKey for this.

One critical point about Themelias aliasing is that it's not technically "rewriting".  Rewriting is an old concept which actually changes the user's request.  So if /problem/([a-z]+) is rewritten to /Problem.aspx?guid=$1, then when /problem/asdf is accessed, HttpContext.Current.Request.Url.AbsoluteUri will show /Problem.aspx?guid=asdf.  Not very helpful.  Therefore, Themelia aliasing thus not rewriting anything, but just mapping a logical path to a physical one, allow the absolute URI to remain as /problem/asdf.

Now since we are talking about variable capture, the data must be captured somewhere.  Given the fact that the above looks like a query string you would think that you would have to access the data as a query string.  However, if that were the case, then you wouldn't actually be able to use a query string (i.e. /problem/asdf?hello=qwer).  Therefore, Themelia provides capture state accessible via Themelia.Web.HttpData.

You may access a single captured item via Themelia.Web.HttpData.GetCaptureItem or you may obtain all captured items via Themelia.Web.HttpData.GetCaptureItemMap( ).

Here's an example of getting the data The following example demonstrates this:

//- $OnSubmit -//
private void OnSubmit(Object sender, EventArgs e)
{
    if (Http.GetUrlPart(Http.Position.Penultima) == "problem")
    {
        //+ in this case, you could get this from Http.GetUrlPart(Http.Position.Ultima) in
        String guid = HttpData.GetCaptureItem("guid");
        Sample.Web.ExceptionSaver.Save(guid, txtDescription.Text);
    }
}

In a world of rewriting, your ability to use query string is just about gone.  Since /problem/adsf IS /Problem.aspx?guid=asdf, /problem/adsf?hello=qwer is very confusing to understand.  Therefore, in Themelia, you may still use a query string.  You may use Themelia.Web.HttpData.GetQueryItem to access a query item in the same place where you use GetCaptureItem(String item) to access a captured item.  Also, as you may have expected, you may use Themelia.Web.HttpData.GetQueryMap to access all query items.

For more information on Themelia HTTP data see the HttpData class documentation.

Initialization Parameters

You may also use Themelia initialization parameters with aliasing.  This essentially means that you can give handlers parameters that they may use for inline initialization.  The handlers have to be aware of the parameters (which is a more advanced topic), but the page alias handler is already initialization parameter aware.  Below is an example of using initialization parameters with the page alias handler.

<handlers>
  <add matchType="contains" name="PageAlias" text="/problem/([\-a-z0-9]+)">
    <parameters>
      <add name="target" value="/Page_/Quality/Problem.aspx?guid=$1" />
    </parameters>
  </add>
</handlers>

The parameters here are the exact same parameters you would set in the <aliases> section.  If you are doing a series of aliases, perhaps you would rather keep them in the <aliases> section, however, if you are only doing one perhaps initialization parameters are more up your alley.

Page Alias Fall Through Processor

(This section requires knowledge of Themelia fall through processors.)

In some situations, perhaps you have an entire web domain or perhaps an entire web site that is full of page aliases and you don't feel like declaring each one of them as handlers.  In this situation, you can use the PageAliasFallThroughProcessor.  The following configuration example demonstrates how to enable this type of scenario:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="pathStartsWith" name="Authentication" text="/authenticate/" />
      </handlers>
      <aliases>
        <!--login-->
        <add key="/login/" target="/Sequence_/Security.aspx" />
        <add key="/logout/" target="/Sequence_/Security.aspx" />
        <!--suggestion-->
        <add key="/contact/" target="/Sequence_/Contact.aspx" />
      </aliases>
      <processors>
        <add type="PageAliasFallThroughProcessor" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

Remember, fall through processors only run if there if there isn't an explicit route for your current path.  Therefore, the /authentication/ path will still go to the Authentication HTTP handler, while everything else will go to the PageAliasFallThroughProcessor.

With these simple page aliasing features, you can kill off all those nasty ".aspx" files off all your pages. That one simple fix will make your entire web site look much more professional and make it much easier for users to remember your links. It also provides a nicely structured web site without a splurge of superfluous physical folders and default documents (i.e. default.aspx) laying all over the place, which, in turn, gives you a more centralized file management experience.

Links

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.

Powered by the Minima Blog Engine 3.1 and Squid Micro-Blogging Library.

Built on Themelia Framework 2.0

Developed using NetFXHarmonics DevServer 1.1.

(all of which are NetFXHarmonics products created by David Betz)

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