Implementing the Front Controller Pattern
The most common approach for implementing the Front Controller pattern in ASP.NET is through an HTTP Module that handles one of the ASP.NET HTTP pipeline events, and executes a
Server.Transfer action to load the appropriate target page. This is the technique implemented in the example application. A dropdown list at the bottom of the
Default.aspx page displays a list of possible targets, from which you can select (see
Figure 8).
 | |
Figure 8. Sample Application Page Targets: The figure shows the list of targets for the Front Controller example displayed in the default page. |
You may recall from the discussion of the Singleton pattern in the
previous article in this series that the example application uses a Singleton class named
TransferUrlList.cs to expose the contents of an XML file that contains the target "short names" (as displayed in the dropdown list in
Figure 8) and the actual URL to load:
<?xml version="1.0" encoding="utf-8" ?>
<transferUrls>
<item name="UseCaseController" url="TransferPage1.aspx" />
<item name="CustomerList"
url="TransferPage2.aspx?view=CustomerList" />
<item name="CustomerDetails"
url="TransferPage2.aspx?view=CustomerDetails" />
<item name="CityList"
url="TransferPage2.aspx?view=CityList" />
<item name="SpecialCustomerList"
url="TransferPage3.aspx?view=CustomerList" />
<item name="SpecialCustomerDetails"
url="TransferPage3.aspx?view=CustomerDetails" />
<item name="SpecialCityList"
url="TransferPage3.aspx?view=CityList" />
<item name="Publish-Subscribe" url="TransferPage4.aspx" />
<item name="Command-Observer" url="TransferPage5.aspx" />
</transferUrls>
To demonstrate the Front Controller pattern, the application contains a class named
FrontController.cs (in the
App_Code folder) that implements an HTTP Module. The class declaration indicates that it implements the IHttpModule interface, which means that it must declare the public methods
Init and
Dispose.
In the
Init method, the class subscribes to the
PreRequestHandlerExecute event by registering an event handler named
MyPreRequestHandler. As the class does not use any unmanaged resources, the
Dispose method requires no actionso it declares only an empty method:
public class FrontController : IHttpModule
{
public void Init(HttpApplication context)
{
// register a handler for the event you want to handle
context.PreRequestHandlerExecute += MyPreRequestHandler;
}
public void Dispose()
{
// no clean-up required
}
...
The ASP.NET HTTP pipeline raises the
PreRequestHandlerExecute event just before it executes the request. The code in the event handler in the FrontController module can therefore make a decision about which page to load based on some external condition. This may be something like the browser language, country, browser type, a value in the user's ASP.NET Profile, or some other value from the environment.
The example module takes simpler approach to make it easier to specify the target you want when experimenting with the application. It examines the query string for a value that corresponds to one in the list of transfer URLs exposed from the XML file, and redirects to the appropriate page. The
GetTransferUrl method of the TransferUrlList class returns the translated URL if found in the list, or the original URL if it is not in the list. The code then executes the
Server.Transfer method to that URL:
private void MyPreRequestHandler(Object sender, EventArgs e)
{
// use features of the request (user, browser,
// IP address, etc.) to decide how to handle the request,
// and which page to show.
// this example looks for specific items in the query
// string that indicate the required target (such as
// "CustomerList") using a dictionary of values loaded from
// an XML disk file
// get Singleton list of transfer URLs
TransferUrlList urlList = TransferUrlList.GetInstance();
// get the current request query string
String reqTarget = HttpContext.Current.
Request.QueryString["target"];
if (reqTarget!= null && reqTarget != String.Empty)
{
// see if target value matches a transfer URL
// by querying the list of transfer URLs
// method returns the original value if no match
String transferTo = urlList.GetTransferUrl(reqTarget);
try
{
// transfer to the specified URL
HttpContext.Current.Server.Transfer(transferTo, true);
}
 | |
Figure 9. Effect of Selecting "CustomerDetails": When you select the "CustomerDetails" item in the dropdown list, the application loads TransferPage2, which displays the CustomerDetails View as shown here. |
catch { }
}
}
When you select one of the "short name" values in the dropdown list in the default page of the application, you'll see the effects of the Front Controller module. For example,
Figure 9 shows the result of selecting the "CustomerDetails" option. The code in the Front Controller HTTP module translates this into the URL
TransferPage2.aspx?view=CustomerDetailsas you can see in the Label control that displays the actual URL of the current page.
Notes on Using a Front Controller HTTP Module
You may be tempted to try using URLs for your Front Controller that include the "short names" as part of the path, rather than in the query string. This will work when you run your application within Visual Studio 2005 because all requests go to the built-in Visual Studio Web server. However, if you run your application under the IIS Web server you will get "Page Not Found" errors, because IIS passes only pages that actually exist to the ASP.NET runtime.
For example, if you use a URL such as
http://localhost/CityList, and there is no folder in your Web site named
CityList, IIS will return a "404 Not Found" error. It will
not initiate the ASP.NET HTTP pipeline, and so your module will not execute. Even if the
CityList folder does exist, IIS will return either a "403 - Forbidden" error, or (if Directory Browsing is enabled) a file listing.
Of course, you could create a folder for each "short name" and put an empty ASPX page in each of these folders so that the Front Controller module redirects to the appropriate page as required. This is better than using pages that contain redirection code (such as
Response.Redirect or
<meta httpequiv="refresh"> elements) in each folder, because the Front Controller will intercept each request and will not actually load the empty ASP.NET page.
An alternative is to map all requests to the ASP.NET runtime DLL, rather than just the standard ASP.NET file extensions such as
aspx and
ascx. However, this is probably not a practical solution because it will result in an excessive processing overhead in ASP.NET.
As you are probably beginning to realize, ASP.NET makes extensive use of design patterns internally, and you can capitalize on design patterns yourself to build logical and easy-to-maintain applications. I'll cover some more advanced patterns in the final article in this series.