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

Themelia Framework 2.0 Beta 4 Released

Thursday, October 16, 2008

Today again marks the point of another major milestone in the development of Themelia Framework 2.0 as I release beta 4.  This release includes many new concepts.  Of these, the major ones are WCF service aliasing, file aliasing, missing trailing slash handling, exception type filters for error processors, object factories, simplified XML configuration, regular expression alias patterning, and the inclusion of Themelia sequences.  There's already a bunch of lessons of using these features on the Themelia web site, so I won't explain any of them here.  However, I will give a quick summary of each below.  For more information on any feature, just check out the documentation on the Themelia web site.

  • A service alias (which requires IIS7 integrated mode) allows you to turn /Service_/Feed.svc/70C707BB-FC85-41ba-8802-30AF4F6AEF3F/10 into /feed.xml.
  • A file alias is essentially a way to alias a file to decouple its logical view (i.e. the path) from the physical file (in other words, it's a symbolic link).
  • Missing trailing slash handling, allows you to set a boolean to accept /framework as well as /framework/ when only /framework/ is installed (and it allows you to require the end slash).
  • You can now tell an error processor which exceptions that it is to handle.  If you want an error processor to only run when an OutOfMemoryException is thrown, just configure it that way.
  • Object factories create any type of object.  This is how, for example, the new exception filtered error processors model may allow you to type "ArgumentNullException" instead of the entire "System.ArgumentNullException, System" (with 172 exceptions ready for use in their abbreviated form-- with the ability for you to add more via object factories).
  • The XML configuration (web) is a lot more streamlined.  Now all processors go in <processors></processors> and all factories go in <factories></factories>.
  • Now you can do stuff regular expression aliasing to do stuff like mapping /product/([a-z0-9]+)/ to /Page_/Information/Product.aspx?sku=$1 and /service/([a-z0-9]+)/ to /Service_/$1.svc.  All captured data that looks like a traditional query string is actually captured in a new Themelia data state called "capture state".
  • Lastly (well, not last, but the last I'm going to mention here), Themelia sequences brings a proven web design pattern to the framework by allowing a centralize controller on a page to load various physically separated views in a workflow manner (e.g. "next", "previous", "first", "last").  Those one sentence explanations aren't nearly enough for anyone so you can checkout the full documentation on each of these by following the links in the above text.

These are just the main points.  There are a bunch of smaller additions, but they will be discussed further in the documentation.

In addition to the release of beta 4, Themelia now has its own home at http://themelia.netfxharmonics.com/.  This web site houses the binaries, the documentation, as well as various other things like a Themelia glossary and a quick reference section.  Because of this web site, Themelia will no longer be found on CodePlex and all future documentation will be posted there.

Themelia Samples are found at http://svn.netfxharmonics.com/Themelia/trunk/Samples/.  This is a web-based subversion repository and while it may be viewed in any browser, it requires TortoiseSVN to download.

Links

Fundamentals of Themelia - File Aliasing

Thursday, October 16, 2008

In addition to page aliases and service aliases, Themelia provides a way to alias single files.  This type of alias is actually rather similar to the concept of symbolic links in Unix-style operating systems.  The basic idea is that there may be one or more logical paths for the same physical resource.  This decoupling of your logical and physical world's allows you to rename a physical file without the rest of the world seeing broken link.

One example of how a file alias could be used can be seen in the major products around the world.  Say there was a major JavaScript framework in the world that publicly releases its single-file framework as ProductName.VersionNumber.js as in my-framework.1.0.4.js.  What happens when this framework becomes my-framework.1.0.5.js?  Now you have to chase your links all over the place to upload them to the new path (and hope the caching catches up sooner rather than later).  Fortunately, this isn't what these guys typically do.  Instead, the people who deploy the framework create an alias called my-framework.latest.js which points to the real file.  Now when the version changes, they only change the connection to that file in some type of internal resource (or in the case of Unix/Linux, change the symbolic link).  They don't have to ever touch the physical endpoint shown to the world.

You can do this same type of this using a Themelia file alias.  Here's an example:

<handlers>
  <add matchType="endsWith" name="FileAlias" text="/download/my-framework.latest.js" />
</handlers>

As with a page alias and a service alias, after the handler has been registered to watch a particular path based on a particular match type, an alias must be registered which is keyed to the handler by handler text.  As you can see, the target of the alias points to the true file.

<aliases>
  <add key="/download/my-framework.latest.js" target="/Resource_/Release/my-framework.1.0.4.js" extra="text/javascript" />
</aliases>

Content Type

Something that you will notice about this is that there's also another attribute, called extra.  This attribute is used differently by different alias types, but, in the case of a file alias, it sets the content type of the resource.  In this example, the physical resource will be streamed to the client as a text/javascript file.

This content type can be anything, but it needs to make sense for what you are doing.  For example, if you make an alias for a zip file, make sure you are sending the file as using application/zip.  When you want to alias a new type of file, be sure to do a quick Google search to see what the appropriate content type is for that particular file type.  One resource to get you started is http://www.webmaster-toolkit.com/mime-types.shtml.  This is a list of content types and how they map to common file extensions.

Initialization Parameters

Now as with page aliases, you can use initialization parameters to setup a file alias.  Here's an example:

<handlers>
  <add matchType="endsWith" name="FileAlias" text="/download/my-framework.latest.js">
    <parameters>
      <add name="target" value="/Resource_/Release/my-framework.1.0.4.js" />
      <add name="extra" value="text/javascript" />
    </parameters>
  </add>
</handlers>

This other type of syntax produces the exact same result, but does so in a more in-line manner.

Loose Coupling

Like with pages and services, one extremely important point about aliases relates to coupling.  If you have a publicly accessible resource whose logical path matches its physical path, you may find yourself in big trouble in the future.  The moment your physical resource moves or is renamed, all links, local and remote, must be changed.  Therefore, if you are versioning public files, perhaps you should think about using an alias from the start.

Links

Introduction to Themelia Sequences

Wednesday, October 15, 2008

Many times with working with a web form, you aren't actually working with a single logical unit.  Sometimes there are different steps to the process which your web forms represents.  Perhaps it's an account wizard, a checkout workflow, or just a contact page with an input and "thank you" page.  In each of these situations you aren't working with a simple web form, but a more complex sequence of pages.  The common solution for this type of situation is design pattern which splits each part of the sequence into its own page with a centralized controller moving the user around.

Themelia brings this abstract design pattern to the concrete world with a concept called a sequence.  This concept, though extremely simple, can seriously improve the manageability of your forms.  In fact, sequences aren't just for wizards, they can help organize any set of related pages.

Once you get used to sequences you may actually find yourself using them more than classic web forms.  Because of this, web site structure sample shown in the introduction to Themelia web site design shows how pages are the minority in Themelia web sites.  Where as sequences become the overall helper for various related pages, individual pages are left for simple single-view pages like a FAQ or an address list.

Creating a Sequence

To begin the discussion on sequences, look at the following sample:

image

Here we see three sequences: Checkout, PasswordReset, and Security.  For now, we are going to zoom out attention into the darkened region, namely, the PasswordReset sequence.

A sequence is created by using sequence page, containing a sequence controller, which contains a series of sequence views.  The sequence page holds the declaration of the different screens that will be shown, the controller simply houses the different views, and the view is the scenario-specific display.

Enough theory, let's go inside of the PasswordReset.aspx sequence:

<%@ Page CodeFile="PasswordReset.aspx.cs" Inherits="Sequence.PasswordReset" %>
<t:SequenceController SequenceName="PasswordReset" ID="Controller" runat="server">
    <t:SequenceView ID="Input" runat="server" />
    <t:SequenceView ID="Success" runat="server" />
</t:SequenceController>

This page represents what just about every sequence will look like: you have registered user controls and a sequence controller with an ID of "Controller".  In this controller there is a required attribute called SequenceName.  This is the name of the sequence and, therefore, also the name of the folder which contains the views.

This sequence has two SequenceView controls, each with an ID suffixed with "View".  Each view ID, minus the "View" suffix" matches to a user control of the same name under the folder declares by the SequenceName property.  For example, in this case, the view controls are "PasswordReset\Input.ascx" and "PasswordReset\Success.ascx".

That's a complete sequence.  Now let's move to the code behind of the sequence itself:

namespace Sequence
{
    public partial class ProblemReset : Themelia.Web.Controls.SequencePage
    {
        //- @SelectInitView -//
        public override String SelectInitView()
        {
            return "Input";
        }
    }
}

Sequence pages inherit from Themelia.Web.Controls.SequencePage and are require to override the SelectInitView method.  This method allows you to set the initial view based on whatever login you decide.  The following example is a contact sequence that should give you a better idea of what kind of logic we're talking about:

<t:SequenceController SequenceName="Contact" ID="Controller" runat="server">
    <t:SequenceView ID="ProblemInput" runat="server" />
    <t:SequenceView ID="ProblemSaved" runat="server" />
    <t:SequenceView ID="MessageInput" runat="server" />
    <t:SequenceView ID="MessageSent" runat="server" />
</t:SequenceController>

Here's the code behind:

namespace Sequence
{
    public partial class Contact : Themelia.Web.Controls.SequencePage
    {
        //- @SelectInitView -//
        public override String SelectInitView()
        {
            String firstUrlPart = Themelia.Web.Http.GetHttpPart(Themelia.Web.Http.Position.First);
            if (firstUrlPart == "suggestion" || firstUrlPart == "contact")
            {
                return "MessageInput";
            }
            else if (firstUrlPart == "problem")
            {
                return "ProblemInput";
            }
            //+
            return "MessageInput";
        }
    }
}

As you can see, when the sequence loads it checks to see if the first part of the URL (http://www.tempuri.org/thispart/) is "suggestion" or "contact".  If so, then the user wants to send a suggestion or contact someone.  However, this same sequence handles public Q/A as well.  Perhaps when an uncaught exception is thrown somewhere in the web site, the user is redirected here to describe what he or she was doing.  If the user is accessing the /problem/ path, then the sequence is told to load that.

The view that is loaded based upon the SelectInitView selection is the name of the view returned plus the suffix of "View.  Therefore, the possibilities in the above SelectInitView method are MessageInputView and ProblemInputView.  It's important to note that if an incorrect view name is returned, then no view will show.  So, if you want a view to show, then be sure to set a default ("MessageInput" in the above example).

Now, what's exactly inside the view?  As previously states, the view directly matches with a user control.  This user control must inherit from Themelia.Web.Controls.SequenceUserControl.  That's the only requirement.  Going back to the password reset sequence, here is the Input.ascx file:

<%@ Control AutoEventWireup="false" Language="C#" Inherits="Sequence.ResetPassword.Input" CodeFile="Input.ascx.cs" %>
<h1>Password Reset</h1>
<div class="contact-input">
    <strong><label for="<%=txtEmail.ClientID %>">E-mail Address</label></strong><br />
    <asp:TextBox ID="txtEmail" runat="server"></asp:TextBox><br />
    <br />
    <asp:Button ID="btnSubmit" runat="server" Text="Go to step 2"></asp:Button>
    <span class="error-message"><asp:Literal ID="litError" runat="server"></asp:Literal></span>
</div>

The code behind we have something like that looks like this:

//- #OnInit -//
protected override void OnInit(EventArgs e)
{
    btnSubmit.Click += new EventHandler(OnSubmit);
    //+
    base.OnInit(e);
}

//- $OnSubmit -//
private void OnSubmit(object sender, EventArgs e)
{
    litError.Text = String.Empty;
    //+
    Boolean success = LoginClient.RunCustomPasswordResetLogic(txtEmail.Text);
    if (success)
    {
        this.MoveToNextView();
    }
}

Just about everything about this is classic ASP.NET.  However, notice that when the password reset is successful, the control makes a call to MoveToNextView.  This is one of four different navigation methods to which both the sequence page and the sequence user control have access.  Here are the navigation methods:

  • MoveToFirstView
  • MoveToLastView
  • MoveToPreviousView
  • MoveToNextView

The function of each of them should be obvious.  In this case, when we know the password reset has been successful, we can simply move to the next view.  Therefore, the order of the sequence views are important to the success of sequence.

View Initialization

One extremely important note about sequences is that when you switch to a new view using one of the above methods, the OnViewInit method is called on the newly loaded sequence user control.  This is very important for wizard scenarios.  For example, say you are on view A and on this view you are asking for the user's first name, last name, and e-mail address.  If you needed any information on the second view, how does it get there?

The recommended solution for this is saving the information is HttpData and loading the information in OnViewInit on the next sequence user control.  For example, do this on view A:

//- $btnSubmit_Click -//
private void btnSubmit_Click(Object sender, EventArgs e)
{
    HttpData.SetScopedItem<String>("Source", "Email", txtEmail.Text);
    //+
    this.MoveToNextView();
}

Then on the next view, do this:

//- @OnViewInit - //
public override void OnViewInit()
{
    litEmailAddress.Text = Themelia.Web.HttpData.GetScopedItem<String>("Source", "Email");
}

Of course, the HttpData class also allows you to more than just strings.  If you needed to transfer a bunch of information, you could store that in HttpData as well.

Reusing User Controls

There's no reason to create a user control for a bunch of different things if they are actually the same.  Image that you had two purposes for an input and success view combo, but the differences in the two purposes requires only that there be a very minor difference between them.  For example, you are working with a Process sequence that has two pages: input and saved.  Further, you "process" you types of things, but the input content for each as well as the saved content for each are the same except for some textual differences (or perhaps a bit of logic setting a control as hidden, or whatever).  In this situation you can could a single input view and a single success view and simply register them multiple times using the ControlName attribute.  Here's an example:

<t:SequenceController SequenceName="Process" ID="Controller" runat="server">
    <t:SequenceView ID="Input" runat="server">
    <t:SequenceView ID="Saved" runat="server">
    <t:SequenceView ID="ComplexInput" ControlName="Input" runat="server">
    <t:SequenceView ID="ComplexSaved" ControlName="Saved" runat="server">
</t:SequenceController>

In this case, the "Process\Input.ascx" and "Process\Saved.ascx" user controls are each used twice.  The logic to figure out what's what would be inside of each control.  Say you had the following configuration:

<themelia.web>
  <webDomains>
    <add defaultPage="/Page_/Security/Home.aspx">
      <handlers>
        <add matchType="contains" name="PageAlias" text="/process/complex/" />
        <add matchType="contains" name="PageAlias" text="/process/single/" />
      </handlers>
      <aliases>
        <add key="/process/complex/" target="/Sequence_/Process.aspx" />
        <add key="/process/single/" target="/Sequence_/Contact.aspx" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

With this, you can easily do something like the following in Input.ascx or Saved.ascx to centralize your views:

//- #OnInit -//
protected override void OnInit(EventArgs e)
{
    if (Themelia.Web.Http.GetHttpPart(Themelia.Web.Http.Position.Second) == "single")
    {
        ddlMyComplexDropDownList.Visible = false;
        litMyComplexText = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
    }
    //+
    base.OnInit(e);
}

Note that you could also use the ControlName attribute for all the views.  This allows you to completely decouple your view names from your control names.

Setting the Page Title

Since a sequence consists of multiple views, each view could potentially require it's own page title.  Fortunately, setting the page title from a user control is as easy as setting the PageTitle property.  It's important to not that in the scope of the ASP.NET page life cycle, this title can be set as late as the OnPreRender method. Here's an example:

namespace Sequence.Contact
{
    public partial class MessageSent : Themelia.Web.Controls.SequenceUserControl
    {
        //- #OnPreRender -//
        protected override void OnPreRender(EventArgs e)
        {
            this.PageTitle = "Thank you for your message";
            //+
            base.OnPreRender(e);
        }
    }
}

You can base the title on whatever logic you choose.  For example, look at the following sample web site structure representing four fictional products:

  • http://www.mydomain.com/product/astra/feedback/
  • http://www.mydomain.com/product/supra/feedback/
  • http://www.mydomain.com/product/ipsum/feedback/
  • http://www.mydomain.com/product/lorem/feedback/

Say each one of these endpoints went to the same sequence.  In fact, this sequence has a view and user control for each product and the sequence automatically selects the proper view in the SelectInitView method based on the URL.  In this scenario we can easily automate our page title by creating a common base class for each user control:

public class FeedbackSequenceUserControl : Themelia.Web.Controls.SequenceUserControl
{
    //- #OnPreRender -//
    protected override void OnPreRender(System.EventArgs e)
    {
        this.PageTitle = Themelia.Case.GetPascalCase(Http.GetHttpPart(Http.Position.First)) + " Feedback";
        //+
        base.OnPreRender(e);
    }
}

This custom user control will set the title to the PascalCased product name plus the word "Feedback".  So http://www.mydomain.com/product/astra/feedback/ will automatically have a page title of "Astra Feedback".

On a different note, you can see in this scenario that sequences aren't just for wizards.  They can provide easy management and organization to related or patterened pages.

Data Binding

When using a sequence, you can do anything you would normally use in a page, this including data binding.  However, sequences give you an extra place to set your data sources.  Admittedly, this isn't really all that exciting, but given the following drop down list...

<asp:DropDownList ID="ddlTopic" runat="server"></asp:DropDownList>

...we can set the data source in the OnDataBinding method like this:

//- #OnInit -//
protected override void OnInit(EventArgs e)
{
    this.AutoDataBind = true;
    //+
    base.OnInit(e);
}

//- #OnDataBinding -//
protected override void OnDataBinding(EventArgs e)
{
    Int32StringMap data = new Int32StringMap();
    ddlTopic.DataTextField = "Value";
    ddlTopic.DataValueField = "Key";
    data.Add(-2, "Select a topic...");
    data.Add(-1, "-------------------------");
    data.Add(1, "General Comment");
    data.Add(2, "Help Request");
    data.Add(3, "Suggestion");
    ddlTopic.DataSource = data.GetDataSource();
    //+
    base.OnDataBinding(e);
}

The AutoDataBind property saves you from checking if IsPostBack is true as binding will only happen when IsPostBack is false.  If you want your data sources to bind during all post backs as well, then set the AlwaysDataBind to true.

This isn't a major abstract as ASP.NET does all the magic for you in classic ASP.NET any ways.  However, this does allow you freedom from IsPostBack mechanics and keeps data binding way from your Load event handler (i.e. Page_Load) method.

As a side note, you could have also used Themelia's MapDropDownList like this:

<t:MapDropDownList ID="ddlTopic" runat="server"></t:MapDropDownList>

With this code behind:

//- #OnDataBinding -//
protected override void OnDataBinding(EventArgs e)
{
    Int32StringMap data = new Int32StringMap();
    data.Add(-2, "Select a topic...");
    data.Add(-1, "-------------------------");
    data.Add(1, "General Comment");
    data.Add(2, "Help Request");
    data.Add(3, "Suggestion");
    ddlTopic.DataSource = data.GetDataSource();
    //+
    base.OnDataBinding(e);
}

With MapDropDownList the text and value fields are automatically set.

Site Wide Registration

One of the beautifies of ASP.NET controls is that you don't have to register them on a per page basis.  If you are going to be using sequences with any regularity, then you should probably register them into your web.config file.  Here's a snippet of that registration for quick reference:

  <system.web>
    <pages>
      <controls>
        <add tagPrefix="t" namespace="Themelia.Web.Controls" assembly="Themelia.Web"/>
      </controls>
    </pages>
  </system.web>

Links

Fundamentals of Themelia - Service Aliasing

Tuesday, October 14, 2008

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

In an effort to provide a clean web experience for users, we nee to remember that URLs aren't the only thing we can alias.  WCF gives us an incredibly powerful service infrastructure that crosses borders into the world of the web.  By creating JSON or XML-based WCF services for web use we can provide for a myriad of solutions and with Themelia we can make those services look good.

Before any examples are given its important to note two important things about this feature:  first, this feature is for web-based services and BasicHttpBinding services, not for non-web related WCF services like federates services, TCP services, or even WsHttpBinding services.  Second, this feature requires IIS7 in integrated mode.

To use this feature, first you need to "install" Themelia into the service host web site by referencing the Themelia.Web assembly, registering the themelia.web configuration section and adding the Themelia routing module to IIS7.  Here's the configuration section registration for quick reference:

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

Also for quick reference is the routing module configuration:

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

At this point, you're ready to use all of Themelia's features.  Of all of these features, one of the most simple to configure is service rewriting.  Here's an example:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="ServiceAlias" text="/service/contact/" />
      </handlers>
      <aliases>
        <add key="/contact/" target="/Contact.svc" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

Now instead of giving out the endpoint /Contact.svc, you just give them /service/contact/.  You could obviously turn the URL in to /contact/ or anything else just as easily.  For now though, let's move to a more extreme example.

In addition to the many different data service mechanisms for which WCF provides, it also supplies awesome REST functionality and an amazing syndication feature.  The combination of these two concepts allows you to create RSS feeds with little effort.  The path to these RSS feeds also accept parameters so your RSS service knows how to generate the feed.  For example, while I use FeedBurner to provide the world with a feed for my blog, I must provide FeedBurner with the original feed.  They and only they have this feed.  The feed I give them looks something like this (no, this isn't really it):

http://www.netfxharmonics.com/Service_/Feed.svc/GetFeed/B166D85E-5B55-42a5-995E-F9D9958406E5/10

The point up to and including the Feed.svc is the service endpoint.  GetFeed is the method I want to call inside my RSS service.  Finally, the guid and the 10 are parameters passing to the GetFeed method.  This method does all the custom logic required for my system to create the feed.  Now notice two things: first, there is no way anyone should ever have to see this URL.  Not only does it contain internal mechanics, it's nearly impossible to remember and its prone to typos.  Second, WCF handles all the fanciness of creating the RSS feed.  If you would like more information on the syndication feature in WCF, see my Squid Micro-Blogging Library.  This library provides the fastest and most manageable way to create RSS feeds around. I've also open sourced it and explained the WCF syndication mechanics of it at http://www.netfxharmonics.com/2008/03/Squid-Micro-Blogging-Library-for-NET-35.

Now regardless if you are creating RSS feeds, the point remains that WCF provides the ability to create REST based services where the URL provides the parameters to the internal method.  Therefore, URLs can get really long, really fast.  Fortunately, as we have already seen, Themelia helps clean these up.  Here's how we can dramatically shrink the above REST-based RSS URL:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="ServiceAlias" text="/feed.xml" />
      </handlers>
      <aliases>
        <add key="/feed.xml" target="/Service_/Feed.svc/GetFeed/B166D85E-5B55-42a5-995E-F9D9958406E5/10" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

All that stuff is now packed into /feed.xml.  You can do the some for the other types of WCF web-based services as well as for WCF BasicHttpBinding services.

Regular Expression Pattern Matching

One important thing you can do with service aliasing (as well as in page aliasing) is use a regular expression to create patterns of matching.  This allows you to setup a single alias for multiple services.  For example, instead of aliasing /service/comment to /Service_/Comment.svc, /service/label to /Service_/Label.svc, and /service/author to /Service_/Author.svc, you may just alias /service/([a-z]+)/([a-z]+) to /Service_/$1.svc/json/$2.

By way of example, let's say this is our WCF configuration:

<system.serviceModel>
  <behaviors>
    <endpointBehaviors>
      <behavior name="JsonPersonServiceBehavior">
        <enableWebScript/>
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <services>
    <service name="Sample.Web.Service.PersonService">
      <endpoint address="json"
                behaviorConfiguration="JsonPersonServiceBehavior"
                binding="webHttpBinding"
                contract="Sample.Service.IPersonService"/>
    </service>
  </services>
</system.serviceModel>

With this we can use the following as our Themelia configuration:

<themelia.web>
  <webDomains>
    <add defaultPage="/Page_/Information/Person.aspx">
      <handlers>
        <add matchType="contains" name="ServiceAlias" text="/service/([a-z]+)/([a-z]+)" referenceKey="svc" />
      </handlers>
      <aliases>
        <add key="svc" target="/Service_/$1.svc/json/$2" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

In this example, we are capturing the service name ($1) as well as the service operation ($2), thus allowing /service/person/GetPersonList to alias "/Service/person.svc/json/GetPersonList, which is exactly what's normally required.  We needed to do it this way because our WCF configuration has set a relative endpoint address ("json") for our service.  Since "json" is part of the internal mechanics of the system that has absolutely no meaning in the real world, the Themelia configuration removes it by capturing around it.

Loose Coupling

One extremely important point about rewriting services relates to coupling.  Tight coupling is a fast way to destruction.  When your physical path matches your logical path, you have a potential disaster on your hands.  When the world is accessing /Contact.svc, what happens when you need to move that file?  People are using this thing, so you can't exactly move it to /Service_/Contact.svc or anything.  Therefore, service rewriting isn't just there to make the URL pretty, it helps save you from insanity later on.

Links

Fundamentals of Themelia - Allowing Direct Page Access

Monday, October 13, 2008

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

When working with Themelia, you always think about the logical paths first and only later think about the internals.  If you are designing a framework, you always write the code to use the framework before you ever build it.  If you are designing the graphical layout of a web site, you focus first on how the user will use the web site and only later make it happen.  Designing the access interfaces to a web site is no different.

Therefore, when storing your physical pages in the standard Themelia locations of /Page_/ and /Sequence_/ (see details on sequences), you can be sure that no user will ever access them directly.  There's no reason to.  To a user, /Contact.aspx is meaningless as is /Page_/Information/Contact.aspx.  A user who wants to contact someone will logical access /contact/.

Because of this, Themelia naturally blocks direct access to /Page_/ and /Sequence_/.  A user who tries to hack your web site by accessing /Page_/Information/Contact.aspx or /Sequence/Checkout.aspx directly will be shown a standard ASP.NET HTTP forbidden error (the same error they would get if they were to access Contact.aspx.cs).  Of course, no user should ever know about these pages as pages are only advertised when they are linked.  You, as the architects and developers, have no reason to provide links to your web site internals.  Think of all ".aspx" files as having the access modifier "internal" (or Friend).

However, if you would like to provide direct access to your internal /Page_/ and /Sequence_/ pages, you may set the allowDirectPageAccess web domain collection configuration property to true.  Here's an example:

<themelia.web>
  <webDomains allowDirectPageAccess="true">
  </webDomains>
</themelia.web>

Though this is possible, it should really only be used for some type of testing or debugging or some type.  No user ever needs to see the internals of a web site.  Therefore, its highly recommended that you always keep this option disabled.

Links

Fundamentals of Themelia - Allowing Missing Trailing Slash

Monday, October 13, 2008

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

Themelia provides you with complete control over how you want your web site to be accessed.  For example, you might declare that /login/, /contact/ and /suggestion/ be access points into your web site.  However, you might, instead, declare /login, /contact/ and /suggestion.  But what about both?  Fortunately, with Themelia you have the option to allow access without the trailing slash through its acceptMissingTrailingSlash option.  Here's an example:

<themelia.web>
  <webDomains>
    <add acceptMissingTrailingSlash="true">
      <handlers>
        <add matchType="startsWith" name="Authentication" text="/authenticate/" />
        <add matchType="startsWith" name="Facebook" text="/callback/facebook/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

Here we are allowing access to the Authentication handler via /authenticate/ or /authenticate and to the Facebook handler via /callback/facebook/ or /callback/facebook.

It's important to remember that this optional is on a per web domain level.  This will allow you to require the trailing slashes for your "service' web domain and declare them as optional for the rest of your web site.  Here's another example:

<themelia.web>
  <webDomains>
    <add acceptMissingTrailingSlash="true">
      <handlers>
        <add matchType="endsWith" type="Redirect" text="/vendor/xyzcorp/">
          <parameters>
            <add name="referral" target="http://www.xyzcorp.tempuri.org/referral/abccorp/" />
          </parameters>
        </add>
      </handlers>
      <fallThroughProcessor>
        <add type="ABCCorp.Web.Routing.ProductParsingFallThroughProcessor" />
      </fallThroughProcessor>
    </add>
    <add name="service" path="service">
      <handlers>
        <add matchType="webDomainPathEquals" name="Authentication" text="/authenticate/" />
        <add matchType="webDomainPathEquals" name="XYZCorp.ThirdParty.RegistrationHttpHandler, XYZCorp.ThirdParty" text="/registration/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

Here we have two web domains: the default ("root") and "service".  The first will accept a request to /vendor/xyzcorp/ and /vendor/xyzcorp for a Themelia redirect, but /service/authenticate/ and /service/registration/ must be accessed directly (...you really don't want more than one endpoint to your service floating around!)

Remember, this is per web domain and, therefore, applies to that which is inside of the web domain.  This means that given acceptMissingTrailingSlash is set to true, while a path of /webdomain/myendpoint/ may be accessed by /webdomain/myendpoint, /webdomain/ is still accessible only by /webdomain.  Therefore, if you would like to allow access to the root of your web domains without the trailing slash, set acceptWebDomainMissingTrailingSlash to true on the <webDomains> configuration collection.  Here's an example:

<themelia.web>
  <webDomains acceptWebDomainMissingTrailingSlash="true">
    <add>
      <!--configuration omitted-->
    </add>
    <add name="service" path="service" defaultPage="/Page_/Service/Home.aspx">
      <handlers>
        <add matchType="webDomainPathEquals" name="Authentication" text="/authenticate/" />
        <add matchType="webDomainPathEquals" name="XYZCorp.ThirdParty.RegistrationHttpHandler, XYZCorp.ThirdParty" text="/registration/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

Here the service web domain root is accessible by either /service/ or /service.

Now, when it comes to URL and service rewriting, everything that has just been mentioned still applies.  However, it's probably a good idea to remind you that the rewrite configuration key must match the text in the handler.  This is how they link together.  Removing the ending slash on the configuration key would disable the rewrite.  For example, the following configuration is correct:

<add acceptMissingTrailingSlash="true">
  <handlers>
    <add matchType="contains" name="UrlRewrite" text="/login/" />
  </handlers>
  <rewrites>
    <add key="/login/" source="/login/" target="/Sequence_/Security.aspx" />
  </rewrites>
</add>

However, the following rewrite would be incorrect for the handler registered above:

<add key="/login" source="/login/" target="/Sequence_/Security.aspx" />

If  you would like to disallow access to the endpoint with the trailing slash you must change both the rewrite key and the handler text.  Again, this is how they link.  The following configuration is also correct:

<add>
  <handlers>
    <add matchType="contains" name="UrlRewrite" text="/login" />
  </handlers>
  <rewrites>
    <add key="/login" source="/login/" target="/Sequence_/Security.aspx" />
  </rewrites>
</add>

Links

Introduction to Themelia Web Site Design

Thursday, October 9, 2008

When we design a web site, we absolutely must remember that the point of the web site has nothing to do with the web site itself or its the internal mechanics.  Therefore, one of the core purposes of Themelia is to put the focus of web development on the perspective of the user.  User's don't care about MyPage.aspx, Login.aspx, Home.aspx or other pieces of internal mechanics, they care about getting information (/information/, /report/, /contact/), making purchases (/products/hats/, /product/shirts/93873XL), and talking with friends (/chat/, /forum/).  Because of this, Themelia allows developers to keep the end in mind when providing a web solution.

In traditional ASP.NET applications, focus is normally placed on the ASPX files first and then later some thought might be put into cleaning up the web site for easy access.  That is, normally we think about default.aspx, login.aspx, and other files and later (if not temporally, then logically) ask the question "Can we alias or rewrite these to clean this up?"  This type of design process is completely backwards.  No one cares about your web site internals.  They only care about the results.  To the end of that result is the interface to the web site.  Here we mean, not the user interface, but how the web site is accessed.

When designing a web application using Themelia, the thought process starts with thinking about path access, not file access.  We also ask questions that developers rarely think about, but that are nonetheless critical: how do we want people to use this particular web site?  How should it be accessed?  What URL would a end user remember?  What API path would help developers understand the point of a particular endpoint?  Do we want to allow access to the entire application?  How can we make it obvious that different parts of a web site truly are different?  What happens when a person is not logged in?  How do I want Google to see my web page? How attractive is my web site structure?  Is the path to the corporate FAQ page easily readable on a business card?

These are questions of usability that, like questions of security, needs to be asked up front, not after the web site has already been designed, or, worst, built.  Themelia helps you create successful web sites by helping you implement solutions based upon answers to these questions.

If you are creating an e-commerce web site, for example, perhaps the answers to the above questions brings you to the conclusion that you should have a logical structure as follows:

http://www.mydomain.com/
http://www.mydomain.com/login/
http://www.mydomain.com/logout/
http://www.mydomain.com/reset/
http://www.mydomain.com/checkout/
http://www.mydomain.com/faq/
http://www.mydomain.com/blog/ (patterned access)
http://www.mydomain.com/blog/2008/09
http://www.mydomain.com/blog/2008/09/September-Newsletter
http://www.mydomain.com/blog/label/hats
http://www.mydomain.com/product/
http://www.mydomain.com/product/shirt/ (patterned access)
http://www.mydomain.com/product/shirt/F1927349/
http://www.mydomain.com/product/shirt/M8172648/

This logical structure has no direct relation to the physical structure at all.  In fact, the physical structure is absolutely irrelevant at this stage of site design and should be considered black boxed.  Fundamentally, what we are looking at with this logical structure is every publicly accessible way of accessing the web site plus two patterned areas.

The first patterned area would be the /blog/ path, which could be Minima (a.k.a Themelia Blog Services).  The second patterned area would be the /product/ path which allows public viewing of various products by product SKU and category type.  In fact, the /shirt/ part of the URL may be completely optional as you obviously have no reason to require it as idea of "shirt" is redundant given the SKU; it would just be there for easy viewing.  Both of these patterned areas as well as the other listed paths are easy to create using Themelia (more on this patterned access topic later on).

Themelia Web Site Structure

Of course, after (and only after) you have a logical structure approved for Internet (and real world) marketing, may create your physical structure.  Though you could do anything you want, Themelia provides a standard convention to follow in order to optimize Themelia usability.  Below is the basic structure of a Themelia web site:

Collapsed Themelia Web Site

Another major point of Themelia is that it provides unobtrusive ASP.NET development.  There's been so much talk about unobtrusive JavaScript in the past few years, it seems that people forgot all about ASP.NET and have let it go to the waste land.  In Themelia, you keep various entities together and keep as much stuff out of the root as possible.  In fact, when using Themelia, you don't even use default.aspx.

The basic folders for Themelia are: Config_, Page_, Resource_, Sequence_, and Service_.  In fact, Resource_ and Service_ are actually hard coded into Themelia to be special folders that may only be used for non-routing purposes (i.e. you can't alias /Resource_/ or /Service_/ or setup a handler in those paths).  Here's the basic run down of the various Themelia folders:

Config_ holds the various configuration sections of the web.config.  There's no reason to keep piling up the configuration in one monolithic file.  It screws with versioning, it kills your ability find elements, and it destroys cohesion.  By utilizing the extremely underused "configSource" attribute on configuration sections, you can greatly unobfuscate your ever growing web.config (or even app.config) files.

Page_ holds the various categories of web forms that your web site will be using.  In Themelia, you don't use web forms in completely the same way you would in classical ASP.NET.  Web forms are still web forms, but not all pages are web forms.  By defaulting your thinking to a web form, you are short circuiting the design process.  Web forms are for rich ASP.NET interaction.  If you need some sexy form of some great grid of data, perhaps you need a web form.  On the other hand, if you are handling a callback from PayPal, you need a handler.  Similarly, if you are going to do a checkout process, you may not need a classical web form, you might actually need a Themelia sequence, a topic discussed in a moment.

Web forms in Page_ are also not accessed directly.  Never will anyone ever access anything in /Page_.  Access to a Themelia web site is only through its logical structure; it's physical structure is protected.  To this end, use Themelia page aliasing for simple Page_ access.  Remember, the point of the system is the logical structure, not the physical structure.  You never want to think of the logical structure as being a byproduct of the physical.  Instead, think of the physical structure being enslaved to the logical.  Furthermore, remember we need to try to ignore the technology as much as possible.  Yes... ignore the technology.  We need to try to focus on what we want, not how to do it.  For example, in the Page_ folder we may have a category of pages relating to security thus creating a /Page_/Security/ folder.  Within the concept of Security, we have the idea of an authenticated home content, the idea of a login, and the idea of a logout.  Translating this to the physical world means, in our /Page_/Security/ folder we may have Home.aspx, which is root page (note: the term "landing page" is meaningless in modern web applications as Google makes ALL pages landing pages) in addition to a page to Login.aspx.  This second page would then handle both login and logout functionality (via page aliasing-- both logical paths map to the same physical path).  It handles the entire idea of a login, which implies the idea of a logout.  Within the page itself we would use Themelia mechanisms to detect which idea the user is accessing.  Thus we may have something like this (note Http and HttpData are in the Themelia.Web namespace and Setting is part of the web application in the Sample.Web namespace):

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

In this example, we are using LINQ to detect what the user wants.  Does this user and login or logout?  Since only /login/ and /logout/ are aliased to this particular page, if its not /logout/, it must be /login/.  Thus, if the user is accessing /logout/, we deauthenticate.  We also check for authentication and redirect to the root if the user is authenticated necessary.  Notice that even when working in the physical world, we are keeping to the ideas in the logical world as much as possible.

Resource_ holds all JavaScript, CSS, and Silverlight files plus any images and videos as well as any master pages for your application.  Not only that, the concept of a master page is a classic ASP.NET idea.  In Themelia, the concept of a "frame" is more appropriate.  There may actually be various frames for a particular web site.  That is, a general overarching look and feel into which content is placed.  Even then, a frame may have its own set of resources.  Perhaps you have a standard frame for most of the web site and an admin frame for the private administrative sections.  The standard frame may have JavaScript and Silverlight files specific for its area and the admin frame may have nothing but a different style sheet.  In any case, the idea of the Resource_ folder is to hold all support resources for your web site.  However, it's not a "dump" zone for all kinds of nonsense.  That place does not exist in any professional application and never actually has.

Sequence_ holds all Themelia sequences.  A sequence is fundamentally a set of interactive ASP.NET files placed in a workflow.  For example, whereas in classical ASP.NET, a contact page would be a web form, in Themelia it's a sequence with a view (a separate control) for input and a view (again, a separate control) for confirmation.  A user comes to the first view of the contact sequence and enters the information.  After the information has been submitted, validated (by classical mechanisms), and saved, the sequence then moves to the next view, which, in this case, may just be a thank you message.  It's a simple concept, but it brings a feel of workflows to forms without needing to bring in a full workflow system to ASP.NET.  Finally, as with items in the Page_ folder, nothing in the Sequence_ folder will ever be directly accessible by the outside world.  Access is through the logical, not the physical structure.  Access to Sequence_ is only through Themelia page aliasing.

Much of the same concepts explained with regard to the Page_ folder apply here as well.  You want to make sure you are focusing on the logical structure, not the physical.  You need to continually ask yourself the question "What is this?"  Not "How should this work?" or "What should I use here?", but "What is this?"  What something is is far more important than what something does.  It's a bit easier to do this in the case of a sequence than it is with a normal page.  The primary reason for this is that sequences enforce a logical internal structure.  That is, all sequences are required to have a method called SelectInitView which always runs the first time the sequence is accessed.  The SelectInitView which you will write will detect what the user wants and set the initial view based on that information.  Here's a sample SelectInitView:

//- @SelectInitView-//
public override String SelectInitView()
{
    String[] urlPartArray = Themelia.Web.Http.UrlPartArray;
    if (urlPartArray.Contains("suggestion") || urlPartArray.Contains("contact"))
    {
        return "MessageInput";
    }
    else if (urlPartArray.Contains("problem"))
    {
        return "ProblemInput";
    }
    //+
    return "MessageInput";
}

In this particular example we have the /suggestion/, /contact/, and /problem/ logical paths being mapped (via page aliasing) to the same physical /Sequence_/Contact.aspx file.  When the sequence loads, it will then check to see what sequence view should load.  In the case of "MessageInput", the MessageInputView will load and will then, in turn, do a check to see if the user requested "suggestion" or "contact".  TO be clear, this isn't the time or the place for a full discussion of Themelia sequences, so simply understand how sequences like pages are to be thought of as supporting the logical structure of the web site.

Service_, as it's name suggests, holds all services.  Since this is 2008, not 2005, your services will probably be WCF services, therefore, .svc files (Themelia requires .NET 3.5 SP1, so if you are using Themelia, you have WCF 3.5.)  Furthermore, in just about every case, your Service_ folder will hold nothing more than .svc files each with only a single line of data.  Below is an example of a WCF service host file.  This is all that's required to get the job done, therefore, that's all that should be there; it's an entire WCF service host.

<%@ ServiceHost Service="Sample.Web.Service.PersonService" Factory="Sample.Web.Service.Activation.PersonServiceHostFactory" %>

 

In addition to the folders, notice also that the root contains just about nothing.  In fact, one thing that's conspicuously absent is the default.aspx file.  That's not to say that there isn't a default, but the default is a page and is, therefore, placed in its proper home in Page_.  If you have a nicely organized structure for all your web forms and forget to move your default.aspx from the root, then you don't actually have a nicely organized structure for all your web forms.  In Themelia, the simple default.htm file signals to the developer as well as to Themelia, that the default.aspx file has been moved.  The Themelia configuration file handles the rest from there (see Default Index Redirect for more information).

Themelia Web Site Structure Example

Now that you have an overview of the various folders in a conventional Themelia web site, let's see a full example of this.  Keep in mind that all of the paths in our previously mentioned logical structure requirements may be handled with the following structure.

Expanded Themelia Web Site Layout

Notice that this physical structure looks nothing like the logical structure.  It shouldn't.  If it does, you're seriously doing something wrong because the logical structure of a web site is for the common person, for their memory, for business cards, and for commercials whereas the physical structure is for the developer to allow him or her to locate and understand physical documents.  For example, whereas the common user will probably be able to remember domain.com/blog/ and domain.com/product/, there's no reason a developer has to put up with /Blog.aspx, /Item.aspx, and myriad other files just haphazardly thrown into the same folder.  What in the world does blog.aspx have to do with product.aspx?  Just about nothing.

The point isn't the technology, the point is the concept each entity represents.  One represents a blog, the other a product.  Therefore, the developer needs to keep them separate so that others may quickly track down the exact type of page.  In the above structure, we see that Blog.aspx (which, by the way, is all Minima requires) and Faq.aspx are declared to be "Information" and Item.aspx is declared to be "Product".  Would you throw all your files into the root of your hard drive? Of course not.  The physical and logical organization structures of a web site have different purposes, different concepts, different audiences, and often times different types (i.e. Checkout.aspx, mapped from /checkout/ is a sequence and Blog.aspx (mapped from Minima) is a page.)  Clean physical organizational structure, while not nearly as important as logical organizational structure, allows for efficient use.

A Deeper Look

There are a few things I would like to point out regarding the above structure.  Let me go through them point by point:

First, notice that the standard frame has special resources that the admin frame wouldn't access.  Since only the standard frame will access it, it makes no sense to have an organization structure that implies that both will.  Therefore, by way of example, the logo is declared to be associated with the standard frame only.

Second, notice that Communication.xap is in the /Resource_/Silverlight folder, not in the /ClientBin folder.  An ASP.NET web site uses the CLR and places special significance on the Bin folder for assembly binding.  Silverlight (we're talking 2.0 here) on the other hand uses the DLR and places no significance on any folder.  A Silverlight application compiles into a xap file, is self contained, and has no requirements on where it should be.  It's a resource, not a special piece of ASP.NET (in fact, it's a client side technology that our PHP friends can use as well.)  Therefore, it's very misleading to have it in the /ClientBin folder as the default suggests some relation to idea behind the /Bin folder (it's my bet that the mere existence of the /ClientBin folder will bring no end to confusion to people.)

Third, notice that the Page_ folder is organized into the various concepts of pages.  Instead of just throwing them into one folder, they are organized for quick mental indexing.

Fourth, notice that the Sequence folder is a pattern of a Name.aspx web form and /Name folder.  The Name.aspx is the definition for the sequence and would be the target of the alias.  The /Name folder, then, houses the different views t