Looking for the site RSS feed? Here it is!

Fundamentals of Themelia - Web Domains

Friday, August 15, 2008

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

In the previous Themelia posts, we've been working only with single-web domain example.  In this post, you'll learn what web domains are and how to create them to dramatically increase the flexibility of your web site.

A web domain is a new Themelia 2.0 concept which is somewhat similar to an AppDomain.  MSDN describes an AppDomain as follows: "Represents an application domain, which is an isolated environment where applications execute."  The keyword here is "isolated" and anyone who has worked intimately work AppDomains you can appreciate their power of isolation.

You often create AppDomains when you want the ability to control what aspect of your application has what functionality (i.e. you cannot unload assemblies from an AppDomain, but you can unload AppDomains). The majority of your application may only have base level functionality, but maybe you want certain financial functionality, perhaps using a plug-in model, in a particular isolated area.  AppDomains are exactly what you need.  Well, Themelia 2.0 web domain analogously brings that functionality to the web (except you don't "unload" web domains; that makes no sense in a web model.)

With any .NET application there will always be at least one AppDomain.  In Themelia, there will always be one web domain, called the "root" web domain.  When you install any part of Themelia, you are installing it into a particular web domain.  HTTP handlers, preprocessors, aliased handler factories, injection processors, midprocessors, fall through processors, post processors, and poststateprocessors are all installed into web domains, not to your entire web site.  However, if you only have one web domain, then functionally these features actually are installed into your entire web site.

When you are using only one web domain, you barely even know you are using one.  For example, in the following simple Themelia configuration, you aren't using a web domain directly, so you forget about its existence:

<themelia.web>
    <webDomains defaultTarget="/Page_/DefaultPage.aspx">
        <add>
            <components>
                <add key="Security" type="Sample.Web.SecurityComponent, Sample.Web" />
            </components>
        </add>
    </webDomains>
</themelia.web>

However, take a look at following configuration based on the Minima 3.1 sample web site, you can see the web domains very clearly:

<themelia.web>
    <webDomains>
        <add>
            <components>
                <add key="Minima" type="Minima.Web.Routing.Component.MinimaComponent, Minima.Web">
                    <parameters>
                        <add name="page" value="~/Default.aspx" />
                        <add name="blogGuid" value="19277C41-7E4D-4AE0-A196-25F45AC48762" />
                    </parameters>
                </add>
            </components>
            <handlers>
                <add matchType="contains" name="PassThrough" text="example.abc" />
            </handlers>
        </add>
        <add name="second" path="/second/" basedOn="root">
            <components>
                <add key="Minima">
                    <parameters>
                        <add name="page" value="~/SecondBlogPage.aspx" />
                        <add name="blogGuid" value="B23115B1-42E8-46A2-88DE-A56CE505E7D0" />
                    </parameters>
                </add>
            </components>
        </add>
    </webDomains>
</themelia.web>

In this example, you can see that there are two web domains.  Minima's core component is installed into the "root" (blank) web domain with one specific configuration and an extra HTTP handler.  Minima's core component is then installed into the "second" web domain with a different specific configuration and with no other features installed.

There are many things to notice about this.  First, notice that web domains require a name and a path.  If there is only one web domain, "root" is assumed, but you may feel free to explicitly set name to "root".  If you are using root, the path is ignored. Second, notice the "basedOn" attribute.  If you are familiar with WPF, then this should feel natural to you.  If not, prepare to learn two things at once.

In WPF, you can create a style for use by various visual entities or by a specific entity type (i.e. TextBox).  This style can have various properties set.  Your style might, for example, have a property setter for FontSize with a value of 14 and another property setter for BorderThickness with a value of 0.  You then create other styles which are based on this style by by setting the "BasedOn" XML attribute, giving the new style all the information from the old.  You may then add your own styles to the new one.  It's very similar to class-based inheritance in object-oriented programming (OOP).

In the same way, when you base one web domain on another, you essentially copy all of it's contents.  In this case, the "second" web domain copies the component information as well as the HTTP handler.  You may object by say "No, you completely declared the component again".  If you were thinking that, you lose some points.  If you look closer, I didn't have to set the component type in the web domain that is based on another web section with a component with the same key.

Also, it's important to note that all the parameters from the component in the "root" web domain copies to the component in the "second" web domain.  In the "second" web domain, I'm not setting the values so much as I am resetting them.  If I didn't override a parameter, the "root" parameter value would be used.  In other words, all inherited parameters are marked as "virtual".

Resetting

Sometimes you may not want to copy all of a web domain's content.  Perhaps you want all of it except for one part.  Fortunately, with Themelia you have that level of flexibility.  You just need to set the reset attribute to true on any part of a web domain which you don't want to copy.  In the following example, the "second" web domain doesn't have the PassThrough handler for example.abc as the "root" web domain did above.

<add name="second" path="/second/" basedOn="root">
    <components>
        <add key="Minima">
            <parameters>
                <add name="page" value="~/SecondBlogPage.aspx" />
                <add name="blogGuid" value="B23115B1-42E8-46A2-88DE-A56CE505E7D0" />
            </parameters>
        </add>
    </components>
    <handlers reset="true" />
</add>

With this mechanism you can have HTTP handlers active for one part of your system and completely nonexistent for another.  You could even have one handler handle one type of request in one web domain and have a completely different handler work in a different web domain.  Just reset that feature before adding your own (for simplicity, you can't remove individual handlers-- that's too much rope!)

Web Domain Inheritance Chain Example

You are allowed to have web domains based on web domains as long own an inheritance chain as you would like.  Perhaps you want folder /x/ to have one web domain and /x/y/ with a different web domain.  One web domain, for example, may handle the ".svc" extension using a custom service handler while a different web domain may handle the same extension using WCF (which, by the way, is done by setting .svc to use the PassThroughHttpHandler).

As I've mentioned before, all Themelia content is copied from one web domain to another.  That ability combined with resetting gives you a tremendous amount of flexibility to control what happens in your entire web site.  You can see an example of this in the following configuration snippet:

<themelia.web>
    <webDomains>
        <add name="finance" path="/finance/">
            <aliasedHandlerFactories>
                <add type="ABCCorp.Web.Routing.CorporateAliasedHandlerFactory, ABCCorp" />
            </aliasedHandlerFactories>
            <accessDenials>
                <add type="address" text="69.147.112.160" message="Go away" target="" enabled="true" />
            </accessDenials>
            <handlers>
                <add matchType="endsWith" name="ReportCreator" text=".xls" />
            </handlers>
        </add>
        <add name="sales" path="/finance/sales/" basedOn="finance">
            <accessDenials reset="true" />
        </add>
        <add name="marketing" path="/marketing/" basedOn="sales">
            <handlers reset="true">
                <add matchType="endsWith" name="ConverstionTracking" text="/tracking/" />
            </handlers>
        </add>
    </webDomains>
</themelia.web>

In this example we have four web domains: "root", "finance", "sales", and "marketing".  Under the /finance/ branch we are watching for any ".xls" access, sending requests to the ReportCreator handler.  We are also blocking any access from a specific IP address.  Under the /finance/sales" branch, like under the /finance/ branch, we are monitoring for ".xls".  However, we aren't blocking any access.  Finally, under the /marketing/ branch we aren't watching for ".xls" access nor are we blocking any addresses, however, we are looking for any access under "/marketing/tracking/", sending it to the ConverstionTracking handler. Finally, these are all sharing the same aliased handler factory to allow http handlers aliasing in a centralized location.

Links

(0 Comments)

Fundamentals of Themelia - Access Denial

Thursday, August 7, 2008

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

In addition to what you may think of as "positive" routing where routing goes to a living place (or even an active BlockHttpHandler), Themelia also provides "negative" routing with its access denial feature.  By using this simple feature you can block certain IP addresses, user agents, or HTTP referrers.  You may send them to a page with a simple message or forward them to another location.

Access denials are in the accessDenial collection and require a denial type, text, and either a message or target.  Here's an example of a very simple IP address denial:

<themelia.web>
    <webDomains>
        <add>
            <accessDenials>
                <add name="localBlock" type="address" text="127.0.0.1" message="Your IP address has been blocked" target="" enabled="true" />
            </accessDenials>
        </add>
    </webDomains>
</themelia.web>

This denial does nothing more than blocks your local host from seeing the web site at all.  If you were to look at any part of the web site from your local host IP address, you would see a simple message saying "Your IP address has been blocked".  If you have a target set, you will simply be redirected to that link.  If both a target and a message is set, the target takes precedence.

The following access denial demonstrates shows how you can use the target attribute to send people away:

<themelia.web>
    <webDomains>
        <add>
            <accessDenials>
                <add name="ie6Denial" type="useragent" text="MSIE 6.0" message="You're fired." target="http://www.getfirefox.com/" enabled="true" />
            </accessDenials>
        </add>
    </webDomains>
</themelia.web>

For example, if a particular forum post is constantly sending you people who wish to do nothing but write YouTube (a.k.a. bathroom wall) style comments on your blog, you can tell them to go away.  Or, better yet, send them away.  When the immature people stop expressing their extreme personal insecurities, you can either delete the entry or disable that entry by setting the enabled attribute to false.

Lastly, It's also important to note that access denials run before anything else in your Themelia pipeline.  Preprocessors won't even run.  So keep that in mind when using these denials.

Links

(0 Comments)

Gems of Themelia - Map and Template

Thursday, July 31, 2008

In addition to Themelia's HTTP routing system, it also includes various other gems.  These gems are often times lay the foundations for other features.  In this post, I would like to discuss two of these gems: the Map and the Template.

At first glance, a Map looks like a regular .NET generic dictionary and in a sense, it is.  In fact, the idea behind a map is that some data is mapped to other data, which is basically what a dictionary is.  The thing that makes a Map a Map, however, is how the data is input and accessed.

A map comes in two varieties, the generic Map<T1, T2> and the Map, which is short for Map<String, String>.  The generic Map contains GetKeyList and GetValueList methods, both of returns a .NET List object.  The generic map also includes a PeekSafely method, which allows you to access data from the Map without having to check if it contains the key.  If the key doesn't exist, the default of the type is returned. In addition to this method, there's also the Pull method, which returns the value of a mapped pair and completely removes it from the Map.

The generic map also contains a constructor which takes a params array of the generic type MapEntry<T1, T2>.  Using this constructor, you can quickly create a generic Map with a set of MapEntry objects.  A MapEntry object is essentially the same as a key value pair.  Of course, in addition to the generic MapEntry<T1, T2> type, there's also a MapEntry, which is actually a MapEntry<String, String>.

The non-generic Map type inherits from the generic Map as Map<String, String>.  This type adds a series of helpful methods to aide in more efficient development and code reading.  Besides having a AddMapEntrySeries, which allows quick addition of many MapEntry types, Map has a number of methods that accept what's called a Map pair, which is a string with the pattern "key=value".  Using the AddPair and AddPairSeries methods, you can add multiple values like this: map.AddPairSeries("FirstName=John", "LastName="Doe"), which, by the way, is the same signature as the constructor.  Here are some Map examples:

Map map = new Map("FirstName=John", "LastName=Doe");

//+ add single pair, pair series, and comma-separated series
map.AddPair("UserName=JohnDoe");
map.AddPairSeries("Email=johndoe@tempuri.org", "WebSite=www.tempuri.org");
map.AddCommaSeries("SpouseName=Jane Doe,Birthdate=04/14/1974");

The Map is used all over Themelia and Themelia-based web sites.  For example, to easily process a query string-like pattern such as that which comes back from a PayPal processing callback, you can easily do something like this:

HttpRequest request = HttpContext.Current.Request;
String data = Themelia.IO.StreamConverter.GetStreamText(request.InputStream);
Map map = new Map(data.Split('&'));
String paypalId = map.Pull("payer_id"); 

Another Themelia type that's used regularly is called the Template.  This type makes code a lot easier to read.  You can setup a template to accept a number of parameters, you can call a template's Interopolate method to create a string based upon that template using the information from a map.  Given the previously shown example of a Map accepting a pair, a pair series, and comma series, the following shows how the template can quickly quick very readable strings:

//+ template with pattern
String pattern = "My name is {FirstName} {LastName}; {Email}; {WebSite}; {UserName}; {SpouseName}; {Birthdate}";
Template person = new Template(pattern);

//+ Interpolate with data with string
Console.WriteLine(person.Interpolate(map));

This example uses replaces the appropriate sections in the template with the actually data in the map.  Compare that to the following:

Console.WriteLine(String.Format("My name is {0} {1}; {2}; {3}; {4}; {5}; {6}", firstName, LastName, email, webSite, UserName, spouseName, birthdate));

The template code is obviously more readable and manageable.  Furthermore, by using this, you can set up a base set of templates and just pass Maps around to create strings in a centralized manner.  Obviously, template patterns could easily be put into resources files too.

Links

(0 Comments)

Advanced Themelia - Dynamically Configuring HTTP Handlers

Monday, July 28, 2008

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

One of the things that expert ASP.NET developers often find themselves doing is carefully inserting processes at just the right place in the ASP.NET pipeline and page life cycle. It's incredibly important for developers to have a strong familiarity with the ASP.NET order of operations. If you do something too early, a control may not exist or session may not be initialized. If you do it too late, any data you set will be overwritten.

Fortunately, if you aren't familiar with these concepts, there are many resources available to us. For example, Rick Strahl has written "A low-level Look at the ASP.NET Architecture" to help developers understand the order of these operations. Furthermore, MSDN contains an overview of the page life cycle eggheadcafe.com has a very detailed reference image.  Then there's a PowerPoint presentation by the queen of all architecture everywhere, Michele Leroux Bustamante, entitled "Inside the ASP.NET Runtime - Intercepting HTTP Requests".  For those of us who are already well versed in the ASP.NET pipeline and page life cycle, these are great reference to which we can turn when we (by we I mean me) forget a thing or two after not doing any pipeline hacking for a while.

Themelia Order of Operations

Within the ASP.NET pipeline and the page life cycle, Themelia runs after all HTTP modules have run, but, because Themelia is the creator of the HTTP handler, before an HTTP handler has been created. This means that you are able to modify HTTP handler properties or even call HTTP handler method before the handler themselves ever run. Granted, the former situation makes more sense than the latter, but the option for method call does exist.

To understand how this is possible, you must first understand the various parts of Themelia as explained in previous posts.  You must also know the Themelia order of operations. Here is a quick run down of the order of operations within Themelia:

  • ConfigurationPreProcessor (loads configuration information)
  • DebugPreProcessor
  • WebDomainPreProcessor (discussed in a future post)
  • IndexRedirectPreProcessor
  • Web Components
  • PreProcessors
  • Aliased Handler Factories
  • Injection Processors
  • MidProcessors
  • If no handler was set yet, HTTP Handler Matching Happens; otherwise skip to postprocessing
  • If no handler is found, the fallthrough processor runs
  • If no handler is found, pass the request through to IIS
  • PostProcessors
  • SessionPostPostProcessor runs
  • PostStateProcessors
  • Continue ASP.NET Pipeline

Dynamically Configuring HTTP Handlers

As this information demonstrates, an HTTP handler will always be set by the time the postprocessors run. Therefore, if you create an HTTP handler containing properties, you may use a postprocessor to configure that handler.  By doing this, when the HTTP handler itself runs, it will have the information required for its own logic.

For instance, if you want to create an efficient Windows Workflow Foundation ("WF") sales reporting workflow activator, you could create a lightweight HTTP handler that did nothing but run that specific workflow. Using Themelia, you may then create a postprocessor designed to configure that specific HTTP handler. This configuration might be nothing more than setting a the workflow activity type.  The following snippet shows what we're talking about:

public class SalesStatisticsWorkflowPostProcessor : Themelia.Web.Routing.PostProcessorBase
{
    public override IHttpHandler OnPostProcessorExecute(HttpContext context, IHttpHandler activeHttpHandler, params object[] parameterArray)
    {
        ReportingWorkflowHttpHandler handler = activeHttpHandler as ReportingWorkflowHttpHandler;
        if (handler != null)
        {
            handler.ReportWorkFlowActivity = "Sales.Workflow.StatisticsActivity, Sales.Workflow";
        }
        //+
        return handler;
    }
}

In this particular example, the HTTP handler is abstracted away from any workflow activity.  It's the post processor that provides this information.  Developers could extend this further by utilizing Themelia web components, creating prepacked processors per department for specific workflow activity sets. Or, if the developers really thought ahead, they could use some of Themelia's supporting helping features to determine what workflow HTTP handler to run based upon a REST-style URL. For example, take a look at the following Themelia postprocessor with its associated registration:

public class SalesWorkflowPostProcessor : Themelia.Web.Routing.PostProcessorBase
{
    public override IHttpHandler OnPostProcessorExecute(HttpContext context, IHttpHandler activeHttpHandler, params object[] parameterArray)
    {
        ReportingWorkflowHttpHandler handler = activeHttpHandler as ReportingWorkflowHttpHandler;
        if (handler != null)
        {
            String pattern = "Sales.Workflow.{0}Activity, Sales.Workflow";
            if (Http.GetHttpPart(Http.Position.Antepenultima) == "report")
            {
                String reportName = Themelia.Case.GetPascalCase(Http.GetHttpPart(Http.Position.Ultima));
                handler.ReportWorkFlowActivity = String.Format(pattern, reportName);
                handler.Region = Http.GetHttpPart(Http.Position.Penultima);
            }
        }
        //+
        return handler;
    }
}

Here is the associated HTTP handler registration for ReportWorkflowHttpHandler (obviously, there would be a aliased handler factory used here, but that's not important for out discussion):

<add matchType="contains" name="ReportingWorkFlow" text="/report/" priority="5" />

In this example, the postprocessor checks to see if the HTTP handler that is going to run is ReportingWorkFlowHttpHandler.  If so, the postprocessor checks to see if the third to the last part of the URL is "report". If this is the case, then the last part of the URL is set as the workflow activity name and the second to the last part of the URL is set as the workflow sales region. For example, "http://www.netfxharmonics.com/workflow/report/signapore/demographic/" would run the "Sales.Workflow.DemographicActivity, Sales.Workflow" while "http://www.netfxharmonics.com/workflow/report/kansascity/annual/" would run the "Sales.Workflow.AnnualActivity, Sales.Workflow". The custom workflow HTTP handler would also have the region information ready to send to WF.

There's one final concluding thought that I would like to mention in passing. Since Themelia provides the SessionPostStateProcessor which allow adding to session before session state is initialized, adding to ASP.NET session is essentially available at every point in Themelia. Therefore, you may feel free to utilize session in your processors as you see fit. In this example, perhaps you would like to create a preprocessor that pulls the penultimate (second to the last) position of the URL and inserts a custom object into session based on that information so all future processors will have it available.

Links

(0 Comments)

Fundamentals of Themelia - HTTP Route Debugging

Thursday, July 24, 2008

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

In the past few posts I've been discussing some of the fundamentals of Themelia. In this post, I would like to discuss a mechanism within Themelia to aide in debug HTTP routing problems.

If you read the previous posts, you know that Themelia features many different HTTP routing concepts (and if you didn't read the posts-- please read them before this post). Each of these provide an addition level of flexibility to the overall flow of routing.

Of these features, injection processors and web components are created as aggregates. With injection processors, for example, you add HTTP handlers to the injection processor by telling the injection processor the full type name of each handler.

However, what happens when this type name is misspelled? In Themelia, nothing happens. The last thing anyone needs is your web site to shutdown just because someone forgot that a particular HTTP handler was moved from the Commerce to the Commerce.Web assembly.

Therefore, you never have to worry about your web site shutting down because of a faulty component. If Themelia did throw exceptions on faulty exceptions the situation would be analogous to a process leaking its fault resources (i.e. memory) into another process.

Now, while it's great that your web site won't explode, without errors, there can be no true debugging. In other words, how in the world can we figure out what's wrong with our routing? To make matters worst, if you are building a complex Themelia web component, the error could be a single letter in the wrong place in any of a dozen of places. So goes all development.

Because of this, Themelia allows you to enable HTTP route debugging. To enable this, just add a debug element under to the routing configuration element, set the enable attribute on debug to true, then set the sender attribute to the type of the Themelia trace sender. Here is an example configuration snippet that tells Themelia to catalog all HTTP routing exceptions and record them to the event log.

<themelia.web>
    <routeDebugging enable="true" sender="Themelia.Trace.EventLogSender, Themelia" />
</themelia.web>

It's literally that simple. Furthermore, Themelia uses it's trace sender mechanism to saves all errors as one report instead of stopping at the first. Therefore, if you have various typos in your web component, you will see as many errors as Themelia sees.

Themelia also has an EmailSender which will send the debug information to your primary web site e-mail. You may also create your own trace sender so you may save the data in different ways. You might, for example, opt to create a DatabaseSender or an FtpSender if those meet your needs better. You can look at the source code to the EventLogSender to see how simple a trace sender is. It's quite literally a matter of overriding a simple method: Save.

Mini-Overview of Themelia Tracing

Themelia route debugging is possible through Themelia's tracing feature, which is a topic for a future discussion. However, it may be good to give a little introduction here. Themelia tracing is based on the concept of a report creator and a trace sender. These two concepts are what differentiate it from .NET's tracing model.

A report creator is any class that inherits from Themelia.ExceptionHandling.ReportCreatorBase. It accepts as parameters any number of custom data structures and uses those data structures to format an output message. One report creator may accept an exception, another may accept an array of strings, and yet another may accept a custom object. This makes it very different from flat formatting mechanisms.

A trace sender is any class that inherits from Themelia.Trace.TraceSenderBase. It works by report queuing and doesn't send anything until it's Save method is called. In Themelia's HTTP routing system, the active trace sender catalogs all routing exceptions and then, when there's nothing else to catalog, it sends the whole package all at once.

If you would like to see an example of Themelia tracing, grab a copy of the Themelia component sample web site, add the above debug configuration element to your web.config, and misspell the Sample web component type name.

Finally, as with index redirection, route debugging is installed into Themelia via a preprocessor and, therefore, gives you yet another example of how to use one.

Links

(0 Comments)

Fundamentals of Themelia - Web Components

Monday, July 14, 2008

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

In a previous Themelia post, I discussed some of the advanced HTTP routing features in Themelia. In this post, I would like to introduce a Themelia concept that ties each of the advanced features together into a single unit: a Themelia Web Component

If you recall, a Themelia injection processor is an entity that automatically installs various HTTP handlers. While that saves you from the trouble of registering each handler one-by-one, you still had to register the injection processor as well as the aliased handler factory and all the various processors yourself.  Web components simplify this.  HTTP injection processors are to HTTP handlers as web components are to all of these features.

In essence, a Themelia web component is a package that automatically registers preprocessors, aliased handler factories, injection processors, midprocessors, postprocessors, poststateprocessors, and a fallthrough processor. The idea is that by creating a web component, a developer may centralize all Themelia routing registration and configuration. Therefore, using a web component, you may now think of web development and HTTP routing in terms of componentization.

One major example of a web component is in Minima 3.1, which is completely packaged into a web component. As you can see from the below configuration snippet, installation of Minima is nothing more than the registration of a Themelia web component.

<themelia.web>
    <webDomains>
        <add>
            <components>
                <add key="Minima" type="Minima.Web.Routing.Component.MinimaComponent, Minima.Web">
                    <parameters>
                        <add name="page" value="~/Default.aspx" />
                        <add name="blogGuid" value="19277C41-7E4D-4AE0-A196-25F45AC48762" />
                    </parameters>
                </add>
            </components>
        </add>
    </webDomains>
</themelia.web>

The registration of a Themelia web component should look fairly straight forward. In the components collection in web.config, just add in your component. Themelia will load the web component and automatically install any Themelia pipeline features that are in the web component.

A Themelia web component is made by creating a class that inherits from Themelia.Web.Routing.Component.ComponentBase. This is an abstract class that requires you to implement the following signature (prepare to scroll!):

public abstract void Register(PreProcessorDataList preProcessorDataList, ProcessorFactoryDataList processorFactoryDataList, AliasedHandlerFactoryDataList aliasedHandlerDataList, InjectionProcessorDataList injectionProcessorDataList, MidProcessorDataList midProcessorDataList, FallThroughProcessorDataList fallThroughProcessorDataList, PostProcessorDataList postProcessorDataList, PostStateProcessorDataList postStateProcessorDataList);

Inside of the Register method, you then add the various Themelia parts to the appropriate list.  This completes the registration of the content of the web components.

Links

(0 Comments)

Fundamentals of Themelia - Default Index Redirection

Tuesday, June 24, 2008

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

Today I would like to introduce an HTTP routing concept of Themelia called default index redirection (or just index redirection). This feature helps me manage my web sites a little better than usual, though it probably won't be that big of a deal to most people. As it stands, though, I personally can't standing having similar files being disorganized all over a folder structure. For example, I have no idea how anyone can put up with having active web forms (web forms being used as web forms) in different folders all over a web site.

Therefore, to help me manage a web site, I like to put my web forms in the /Page_/ folder and let Themelia do all the URL rewriting. Even then, there's still that pesky default document in the root. Not only does that break my pattern of active web forms, but I still have a page that requires management in the root of my web site. No thanks.

Therefore, Themelia gives the ability to move your default document to another location so all active web forms may live together in peace. This is possible through Themelia's index redirection feature.

To use this feature, set the defaultTarget attribute on the Themelia rewrite element to your true default document. Then, to trick IIS into loading the web site, create a blank file in your root called Default.htm; this also means you will need to add Default.htm as a default document in IIS. The following Themelia configuration snippet demonstrate the setup:

<themelia.web>
    <webDomains defaultTarget="/Page_/DefaultPage.aspx">
    </webDomains>
</themelia.web>

To stop the flow of about a million questions, yes, this feature is functionally the same as putting a Service.Transfer("/Pages_/DefaultPage.aspx") in your default document. However, this couples configuration directly with your web application. Furthermore, the point of using this default.htm file is to signal to the world that it's not to be used. Otherwise, some intern will come along and add more code to your /default.aspx file. Therefore, in my web sites, the default.htm stands as a big "Go Away" sign containing only the test "This page intentionally left blank."

Internals

The index redirection feature is a prime example of a preprocessor and midprocessor working together. Internally, Themelia runs IndexRedirectPreProcessor::OnPreHttpHandlerExecute to check to see if the request is for the default document and if index redirection is active. This is actually the internal Themelia preprocessor that uses the parameter array as the following snippet shows:

new IndexRedirectPreProcessor().OnPreProcessorExecute(context, midProcessorList);

IndexRedirectPreProcessor will then do a few checks to see if index redirection should occur. If so, IndexRedirectPreProcessor adds IndexRedirectMidProcessor to the midProcessorList parameter so that Themelia will load IndexRedirectMidProcessor when it runs though midprocessors. IndexRedirectPreProcessor also sets two HTTP context items so as to communicate with IndexRedirectMidProcessor and the URLRewriteHttpHandler, one item stating that URL rewriting must occur and the other stating the true index redirection path.

Later when Themelia runs through the various midprocessors, it will run the previously registered IndexRedirectMidProcessor. IndexRedirectMidProcessor then checks to see if IndexRedirectPreProcessor has requested it to start URLRewriting. If so, it returns an instance of UrlRewriteHttpHandler. If you recall, midprocessors have the power to basically skip over the HTTP selection process. In this case, by returning an instance of UrlRewriteHttpHandler to Themelia, IndexRedirectMidProcessor is effectively telling Themelia to run URL rewriting no matter what.

When the UrlRewriteHttpHandler runs, it checks the other context item set by IndexRedirectPreProcessor to see if an index redirect path was found. If so, the root default document (default.htm) is rewritten to true real default document.

This information should help you to further understand how you may further exploit Themelia's advanced HTTP routing features.

Links

(0 Comments)

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

Powered by the Minima Blog Engine 3.1, NetFXHarmonics Prominax, and Squid Micro-Blogging library.

Built on Themelia Framework 2.0

Developed using NetFXHarmonics DevServer 1.0.

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