Monday, May 18, 2009

A Complete URL Rewriting Solution for ASP.NET 2.0

by Gaidar Magdanurov

This article describes a complete solution for URL rewriting in ASP.NET 2.0. The solution uses regular expressions to specify rewriting rules and resolves possible difficulties with postback from pages accessed via virtual URLs.

Why use URL rewriting?
The two main reasons to incorporate URL rewriting capabilities into your ASP.NET applications are usability and maintainability.

Usability
It is well-known that users of web applications prefer short, neat URLs to monstrous addresses packed with difficult to comprehend query string parameters. From time to time, being able to remember and type in a concise URL is less time-consuming than adding the page to a browser's favorites only to access later. Again, when access to a browser's favorites is unavailable, it can be more convenient to type in the URL of a page on the browser address bar, without having to remember a few keywords and type them into a search engine in order to find the page.

Compare the following two addresses and decide which one you like more:

(1) http://www.somebloghost.com/Blogs/Posts.aspx?Year=2006&Month=12&Day=10
(2) http://www. somebloghost.com/Blogs/2006/12/10/

The first URL contains query string parameters to encode the date for which some blog engines should show available postings. The second URL contains this information in the address, giving the user a clear idea of what he or she is going to see. The second address also allows the user to hack the URL to see all postings available in December, simply by removing the text encoding the day '10': http://www.somehost.com/Blogs/2006/12/.

Maintainability
In large web applications, it is common for developers to move pages from one directory to another. Let us suppose that support information was initially available at http://www.somebloghost.com/Info/Copyright.aspx and http://www.somebloghost.com/Support/Contacts.aspx, but at a later date the developers moved the Copyright.aspx and Contacts.aspx pages to a new folder called Help. Users who have bookmarked the old URLs need to be redirected to the new location. This issue can be resolved by adding simple dummy pages containing calls to Response.Redirect(new location). However, what if there are hundreds of moved pages all over the application directory? The web project will soon contain too many useless pages that have the sole purpose of redirecting users to a new location.

Enter URL rewriting, which allows a developer to move pages between virtual directories just by editing a configuration file. In this way, the developer can separate the physical structure of the website from the logical structure available to users via URLs.

Native URL mapping in ASP.NET 2.0
ASP.NET 2.0 provides an out-of-the-box solution for mapping static URLs within a web application. It is possible to map old URLs to new ones in web.config without writing any lines of code. To use URL mapping, just create a new urlMappings section within the system.web section of your web.config file and add the required mappings (the path ~/ points to the root directory of the web application):






Thus, if a user types http://www.somebloghost.com/Support/Contacts.aspx, he can then see the page located at http://www.somebloghost.com/Help/Contacts.aspx, without even knowing the page had been moved.

This solution is fine if you have only two pages that have been moved to other locations, but it is completely unsuitable where there are dozens of re-located pages, or where a really neat URL needs to be created.

Another possible disadvantage of the native URL mapping technique is that if the page Contacts.aspx contains elements initiating postback to the server (which is most probable), then the user will be surprised that the URL http://www.somebloghost.com/Support/Contacts.aspx changes to http://www.somebloghost.com/Help/Contacts.aspx. This happens because the ASP.NET engine fills the action attribute of the form HTML tag with the actual path to a page. So the form renders like this:

action="http://www.simple-talk.com/Help/Contacts.aspx" id="formTest">


Thus, URL mapping available in ASP.NET 2.0 is almost always useless. It would be much better to be able to specify a set of similar URLs in one mapping rule. The best solution is to use Regular Expressions (for overview see Wikipedia and for implementation in .NET see MSDN), but an ASP.NET 2.0 mapping does not support regular expressions. We therefore need to develop a different solution to built-in URL mapping.

The URL rewriting module
The best way to implement a URL rewriting solution is to create reusable and easily configurable modules, so the obvious decision is to create an HTTP Module (for details on HTTP Modules see MSDN Magazine) and implement it as an individual assembly. To make this assembly as easy to use as possible, we need to implement the ability to configure the rewrite engine and specify rules in a web.config file.

During the development process we need to be able to turn the rewriting module on or off (for example if you have a bug that is difficult to catch, and which may have been caused by incorrect rewriting rules). There should, therefore, be an option in the rewriting module configuration section in web.config to turn the module on or off. So, a sample configuration section within web.config can go like this:


true





This means that all requests that run like: http://localhost/Web/2006/12/10/ should be internally redirected to the page Posts.aspx with query string parameters.

Please note that web.config is a well-formed XML file, and it is prohibited to use the symbol & in attribute value strings. In this case, you should use & instead in the destination attribute of the rule element.

To use the rewriteModule section in the web.config file, you need to register a section name and a section handler for this section. To do this, add a configSections section to web.config:







This means you may use the following section below the configSections section:



true






Another thing we have to bear in mind during the development of the rewriting module is that it should be possible to use 'virtual' URLs with query string parameters, as shown in the following: http://www.somebloghost.com/2006/12/10/?Sort=Desc&SortBy=Date. Thus we have to develop a solution that can detect parameters passed via query string and also via virtual URL in our web application.

So, let’s start by building a new Class Library. We need to add a reference to the System.Web assembly, as we want this library to be used within an ASP.NET application and we also want to implement some web-specific functions at the same time. If we want our module to be able to read web.config, we need to add a reference to the System.Configuration assembly.

Handling the configuration section
To be able to read the configuration settings specified in web.config, we have to create a class that implements the IConfigurationSectionHandler interface (see MSDN for details). This can be seen below:

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;using System.Web;
using System.Xml;
namespace RewriteModule
{
public class RewriteModuleSectionHandler : IConfigurationSectionHandler
{
private XmlNode _XmlSection;
private string _RewriteBase;
private bool _RewriteOn;
public XmlNode XmlSection
{
get { return _XmlSection; }
}
public string RewriteBase
{
get { return _RewriteBase; }
}
public bool RewriteOn
{
get { return _RewriteOn; }
}
public object Create(object parent,object configContext,System.Xml.XmlNodesection)
{
// set base path for rewriting module to application root
_RewriteBase = HttpContext.Current.Request.ApplicationPath + "/";
// process configuration section from web.config
try
{
_XmlSection = section;
_RewriteOn = Convert.ToBoolean(section.SelectSingleNod("rewriteOn").InnerText);
}
catch (Exception ex)
{
throw (new Exception("Error while processing RewriteModule configuration section.", ex));
}
return this;
}
}
}

The Class RewriteModuleSectionHandler will be initialized by calling the Create method with the rewriteModule section of web.config passed as XmlNode. The SelectSingleNode method of the XmlNode class is used to return values for module settings.

Using parameters from rewritten URL
When handling virtual URLS such as http://www. somebloghost.com/Blogs/gaidar/?Sort=Asc (that is, a virtual URL with query string parameters), it is important that you clearly distinguish parameters that were passed via a query string from parameters that were passed as virtual directories. Using the rewriting rules specified below:



you can use the following URL:

http://www. somebloghost.com/gaidar/?Folder=Blogs

and the result will be the same as if you used this URL:

http://www. somebloghost.com/Blogs/gaidar/

To resolve this issue, we have to create some kind of wrapper for 'virtual path parameters'. This could be a collection with a static method to access the current parameters set:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;using System.Web;
namespace RewriteModule
{
public class RewriteContext
{
// returns actual RewriteContext instance for current request
public static RewriteContext Current
{
get
{
// Look for RewriteContext instance in current HttpContext. If there is no RewriteContextInfo item then this means that rewrite module is turned off
if(HttpContext.Current.Items.Contains("RewriteContextInfo")) return (RewriteContext)
HttpContext.Current.Items["RewriteContextInfo"]; else
return new RewriteContext(); } } public RewriteContext() { _Params = new NameValueCollection(); _InitialUrl = String.Empty; } public RewriteContext(NameValueCollection param, string url) { _InitialUrl = url; _Params = new NameValueCollection(param); } private NameValueCollection _Params; public NameValueCollection Params { get { return _Params; } set { _Params = value; } } private string _InitialUrl; public string InitialUrl { get { return _InitialUrl; } set { _InitialUrl = value; } } }}
You can see from the above that it is possible to access 'virtual path parameters' via the RewriteContext.Current collection and be sure that those parameters were specified in the URL as virtual directories or pages names, and not as query string parameters.

No comments:

Post a Comment

Google Ads by Mavra

About Me

My photo
Jeddah, Makkah, Saudi Arabia
for more details www.inhousetoday.com