Mark Mintoff My superpower is common sense

22Mar/112

ASP.Net URL Rewriting

URL Rewriting is a methodology for creating dynamic, customizable "fake" URLs pointing to a real URL. This is tremendously useful:

  • URLs may be made more user-friendly and easier to remember
  • Links are more professional looking
  • Small bonus to search engine optimization
  • Possibility to offer language support
  • Hiding querystring parameters
  • Creating URLs which do not contain the page's file extension (.aspx, .html, .shtml, .php...etc)

But how can we achieve this using ASP.Net C#? For starters, it is worth knowing that IIS 5.0 - 7.0 support URL Rewriting. IIS 7.0 supports URL Rewriting seamlessly, whereas IIS 5.0 and 6.0 require a specific configuration, which I will discuss later in the post.

The URL Rewriting Module needs to be developed under it's own separate project within your website's solution. This is necessary, as there are some web.config configurations to be written, which will require a reference to that project. For the purposes of simplicity, I will create a URL Rewrite module from scratch which will work using a hard-coded collection. For your own project you may use an XML file or preferably a database implementation.

So first things, first: I created a web application project (ProtoURLRewrite) and added a class library project (Proto.Core.URLRewrite). I added a reference to System.Web to Proto.Core.URLRewrite and added a reference to Proto.Core.URLRewrite from ProtoURLRewrite. I then created a class (RewriteModule.cs) in Proto.Core.URLRewrite which inherits from IHttpModule.

I then added two event handlers, for BeginRequest and PreRequestHandlerExecute. Since for simplification purposes, I will be using hard-coded URLs, I also added a static Dictionary and populated some values. Currently, the class looks like this:

At this stage it would be worth mentioning how this will be working. All requests (and that includes file requests such as .css, .js, .jpg...etc) will go through our RewriteModule. It is perfectly fine to let these file requests slip through the logic, however to improve performance, I added a bit of Regex to filter out those file requests.

What we are doing now is looping through our collection of URL Rewrite "rules"; again I recommend doing this by using a database. If the requested path (ex: "/mainpage") matches the Key one of the entries, the requested path is set to be the Value from the entry (ex: "/Default.aspx"). Additionally, if there is an existing query string (ex: "?lang=en") it is appended to the url (ex: from "/mainpage?lang=en" to "/Default.aspx?lang=en"). After all of that is done, the original raw url is set on the a context item for later use and the path is rewritten.

 

For the purposes of being able to handle querystrings, we will now create a RewriteContext object:

This object will act as a querystring proxy, and for future querystring processing, we would need to query both the RewriteContext and the regular Request.QueryString in order to find our querystring. For this purpose, I have created an extension (which makes use of some other extensions I have created):

This extension will not only return the query string value from the RewriteContext and Request.QueryString, but will also return it in the specified type <T>.

Within the context_PreRequestHandlerExecute event, we shall now write the following code:

What we are doing here is creating a RewriteContext object to be used instead of the querystring. Additionally, we are changing the Form's action to the rewritten path, so that after a postback is done, the path remains the same. Only one thing left before we can test: web.config settings:

  1. Under the <system.webServer> tag, enter the following details:
    <validation validateIntegratedModeConfiguration="false" />
  2. In the <modules> tag, which resides within the <system.webServer> tag, add the following attribute:
    runAllManagedModulesForAllRequests="true"
  3. Under the <modules> tag which resides within the <system.webServer> tag, add the following:
    <add name="RewriteModule" type="Proto.Core.URLRewrite.RewriteModule,Proto.Core.URLRewrite />
  4. Under the <httpModules> tag which resides within the <system.web> tag, add the following:
    <add name="RewriteModule" type="Proto.Core.URLRewrite.RewriteModule,Proto.Core.URLRewrite />

For clarity:

Finally, I get to test. Within the Default.aspx's Page_Load event I have added the following bit of code:

/pageOne resolved to the id being 1
/pageTwo resolved to the id being 2
/pageThree resolved to the id being 3

The test has been a success. One final point to mention is IIS configuration for 5.0 and 6.0. As previously mentioned, IIS 7.0 will integrate seamlessly so the following steps are not required:

  1. Open inetmgr.exe
  2. Right click your website -> Properties
  3. Select the Virtual Directory tab
  4. Click Configuration
  5. Select the Mappings tab
  6. Click Insert
  7. Type in C:WINDOWSMicrosoft.NETFrameworkv2.0.50727aspnet_isapi.dll
  8. Uncheck Verify that file exists
  9. Click OK
  10. Click OK
VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
Comments (2) Trackbacks (0)
  1. Hi Mark, in the function "context_BeginRequest", you can rather than iteratting through the "urlDataSource" variable, you could make it hook up to a database to look up hashed versions of your URL? I know you could not add it to the example given since it complicates it.

    • Hi Andrew,

      Yes I would in fact suggest replacing the urlDataSource Dictionary variable with a proper database implementation, or alternatively (in situations where the URLs will not change frequently) through the utilization of an XML file. For a database implementation, I would recommend caching the database values in a static collection. I will blog about that aspect, and the caching model I created, in the near future.

      I wouldn't hash up a version of the URL within the database, rather I would construct a table consisting of key value pairs (which is why I selected a Dictionary as my dummy datasource). The columns I would suggest are:

      ID uniqueidentifier
      PatternToMatch varchar(MAX)
      UnderlyingPath varchar(MAX)
      Deleted bit


Cancel reply

No trackbacks yet.