What Is URL Rewriting and Why Do I Care?
Usability and Maintainability are the two main reasons for Url rewriting, as Gaidar Magdanurov pointed out on his blog at http://www.simple-talk.com/dotnet/asp.net/a-complete-url-rewriting-solution-for-asp.net-2.0/ some five years ago:
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 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.
Which of these urls do you like best?
- http://www.davesweb.com/default.aspx?RT=ViewItem&Id=32&Region=NiteSide&Group=Main
- http://www.davesweb.com/NiteSide/Main/Item32.aspx
The first Url contains query parameters to tell the server what type of request this is and to provide any additional information to help the server locate the correct document and display it to the user. The second Url contains virtually the same information, but is in a path/folder format that is more readable and gives the user a better idea that this Url points to.
As far as maintainability goes, Url rewriting allows, among, webmasters to redirect users to a file’s new location. Let’s say the DavesWeb contact form used to be at /Contact.asp and now it’s at /ContactForm.aspx. In the past, /Contact.asp content would be replaced with a method that will automatically refer the user to the new location. The obvious problem is that we have to continue to maintain a virtually useless file at /Contact.asp. And on a site of any size, over time we will have to maintain hundreds of virtually useless pages like this.
Url rewriting is a solution. With its release of ASP.Net 2.0, users of Microsoft servers had an out-of-the-box solution for mapping static urls within a web application. There was no code to write. Just create a urlMappins section within the system.web section of your web.config file.
Url mappings added to a web.config file
So when a user types in http://www.DavesWeb.com/Support/Contacts.aspx, she will be automatically served up the page at http://www.DavesWeb.com/Help/Contacts.aspx without ever even noticing. No bookmarks to update. No old redirect pages to maintain.
This is great if you have only a handful of static pages to map. But what if you have built an application like DavesWeb that uses query string parameters or if you have hundreds and hundreds of urls that need mapping to their new location, this ain’t going to fly.
Among the solutions is the above mentioned Gaidar Magdanurov’s RewriteModule.dll which allows you to declaratively set up mapping rules in your web.config file that take advantage of the power of Regular Expressions. Regular Expressions are a fast and powerful method of searching and replacing text.
Gaidar Magdanurov's customized rule declaration, also in web.config, but coupled with his custom component
In this example, the rule will take a Url like “2012/11/11” and populate query string parameters like “Posts.aspx?Year=2012&Month=11&Day=11.
Unfortunately, as the comments section of Gaidar’s post shows, I, like many people, had success with the module when running locally on their development machines, but when they tried to run it on a QA or production server, there were configuration issues. That was my story—it worked perfectly running locally but there were configuration problems when I tried it on my production server.
Rather than spending an unknown amount of time trying to figure out the configuration issues and running the risk that I was unable to get it running at all, I decided to devote some time to my own Url rewriting solution.
I am able to go down this path because I built DavesWeb to run independently of many of ASP.Net’s bells and whistles. All of the site’s rendering is managed by the DavesWeb.dll module. I don’t use any of ASP.Net’s WebForms and PostBack functionality. And I’m glad. Because I won’t have to fool with some of the issues Gaidar had to deal with on his site which does use ASP.Net PostBacks.
First, I created my versions of the rewriting rules I would need on my site. Given the way DavesWeb is organized, I will be able to get by with just three rules: one for the two top-level Regions (DaySide and NiteSide), one for the second-level Groups (Main, Gallery, etc), and one for individual posts.
Regular expression rules for url mapping on DavesWeb.com
The first rule is for individual posts. It takes a friendly Url like “/NiteSide/Gallery/Item74.aspx” and turns it into the query the server understands “/default.aspx?RT=ViewItem&Id=74&Region=NiteSide&Group=Gallery”. The second takes a friendly Url like “/NiteSide/Gallery/Index.aspx” and turns it into “/default.aspx?RT=GroupHome&Group=Gallery” and the third takes a friendly Url like “/NiteSide/Home.aspx” and turns it into “/default.aspx?RT=RegionHome&Region=NiteSide”. The key to note is that the presence of ItemXX.aspx signals that this is an item query, Index.aspx signals that this is a Group index query, and Home.aspx signals that this is a Region Home Page query.
This Xml data goes into the file xmlAppSettings.xml that is stored under the App_Data folder of the website and is machine readable and writable. This file contains many settings used by the site. At startup, these settings are loaded into Application variables so they can quickly be accessed when needed.
To read and apply these rules, I wrote a static class called UrlReWriter that includes a method named ReWriteUrl.
From DavesWeb.UrlReWriter class C# code
If a rule in the set finds a match, the url is rewritten using the Regular Expression for that rule. Otherwise, it returns the same Url it was passed (and whether the server is able to fetch the request remains to be seen).
UrlReWriter is implemented in the Global.asax file’s Application_BeginRequest method.
Code added to Global.asax (There was not an Application_BeginRequest implementation prior to this)
UrlReWriter.ReWriteUrl is called, and if the reWrittenPath differs from the original RawUrl, we send the user to the new location with the RewritePath() of System.Web.HttpContext.Current.
Now I want all my internal links to use the path/folder format instead of the query string format. I started to write a method that would take the query string request and turn it into the friendly path/folder format. But I realized there were only a handful of places on the site where I needed to update this code, so changed the individual url-writing code rather than build old-style query string urls and pass them to a method that would have rewritten them in the friendly format. For example, individual posts are loaded via the ItemDataSource class. The code used to be:
and now it’s
Similar code that forms friendly urls have replaced query string urls in a handful of places in the DavesWeb.dll module
That’s all there is!
After having this system up and running for just a few days, I am realizing some additional advantages of Url rewriting. See, I thought I was being clever when I first build DavesWeb to use a minimal number of .aspx pages. Public users base their queries on default.aspx, the RequestType query string parameter shows what kind of request this is and additional parameters give the server all the information it needs to complete that query. Unfortunately, this means that the software my web host provides for site statistics does not take the query string into account. All of the queries to my site, no matter what, show up as the same page/url in my reports. Url ReWriting (as long as all requests are in the non-query folder format) allows individual posts and the Group and Region home pages to show up separately in my reports. Likewise, the Facebook integration code now allows a “Like” to be differentiated among the different post, when prior to Url rewriting any like anywhere on my site showed up as a like for the home page, not the individual posts.