Short, simple URLs improve a site’s search engine optimization, usability, and security.  URL rewriting and routing can be used to accomplish this.  URL rewriting/routing improves SEO by distilling URLs down to a set of keywords that search engines can easily parse.  It improves usability by abstracting the file structure of the site, which may change, and presenting a simplified site structure.  It improves security by hiding the details of how a site is implemented by removing querystring and file suffixes.

Before the release of IIS7 and .Net 3.5, most ASP.Net developers implemented URL rewriting by one of the following methods:

1.       Implementing ISAPI filters for IIS

2.       Developing their own HttpModule

3.       Licensing a configurable third party ISAPI filters like ISAPI Rewrite, or HttpModule like UrlRewriter.Net.

With the introduction of IIS7, Microsoft released an add-on module that implements URL rewriting.  The URL Rewrite module works similarly to ISAPI filter based URL rewriting, but has the advantage of being more closely integrated to the web server.  The configuration is XML driven by settings added to the web.config.  The module can map URLs based on regex pattern matching, or by a list of 1-to-1 URL mappings.  In addition, configuration can be done through the IIS7 GUI.

The diagram below shows how URL rewriting using the URL Rewrite Module for IIS7 works.



Pros:

·         Easy to implement

·         Mapping changes do not require a recompile and code deployment

·         Management through IIS7 UI

Cons:

·         Can’t be integrated with a CMS or database driven URL maps

·         No built-in functionality for reverse lookups

·         Locks you in to using IIS7

.Net 3.5 introduced ASP.Net routing, a new way of mapping URLs to HttpHandlers.  ASP.Net routing works differently from any form of URL rewriting.  URL rewriters change an incoming request’s URL and then hands off the request.  ASP.Net doesn’t change the request URL.  Instead, it loads HttpHandlers directly based on its mapping logic.

The following diagram shows how ASP.Net rewriting fits into the request processing pipeline.



Pros:

·         Easier to integrate with CMS or database driven URL mappings

·         Reverse mapping is easier if you build for it

·         Custom implementation offers greater flexibility – like giving developers a neat way of doing A/B testing

Cons:

·         Easy to turn into spaghetti code

·         Changes to mapping logic often require a recompile and code redeploy

·         Takes more effort to implement

For more explanation about the differences between these two methods, read this article.

These two methods can be combined to get some of the advantages of each.  The following example involves an ASP.Net site that contains pages managed by a content management system.  We’d like to have some of the ease of implementation, and maintenance from the IIS module, but we also need to allow some URL mappings to be controlled by the CMS.

The following shows the web.config settings for the IIS Rewrite Module.  The first rule applies the 1-to-1 mappings in the Custom Mappings rewriteMap section.  The second rule routes page requests to four main categories within the site to paths that the ASP.Net URL rewriter module will handle later on in the request lifecycle.

<rewrite>
       <clear />
       <rule name="Apply Custom Rewrite Map" enabled="true" stopProcessing="true">
              <match url="(.*)" />
              <conditions logicalGrouping="MatchAll">
                     <add input="{Custom Mappings:{PATH_INFO}}" pattern="(.+)" />
              </conditions>
              <action type="Rewrite" url="{C:0}" appendQueryString="true" />
       </rule>
       <rule name="Product Type Pages" stopProcessing="true">
              <match url="^(Music|Movies|Games|Accessories)(/)?$" />
              <conditions logicalGrouping="MatchAll" />
              <action type="Rewrite" url="Pages/{R:1}" />
       </rule>
       <rule name="All Other Pages" enabled="true" stopProcessing="true">
              <match url="^([_0-9a-z-]+)(/)?$" />
              <conditions logicalGrouping="MatchAll" />
              <action type="Rewrite" url="Pages/{R:1}.aspx" />
       </rule>
       <rewriteMaps>
              <rewriteMap name="Custom Mappings">
              <add key="/Home" value="Pages/Default.aspx" />
              <add key="/Search" value="Pages/Search/Default.aspx" />
              <add key="/Admin" value="Pages/Admin/SiteAdmin.aspx" />
       </rewriteMap>
       </rewriteMaps>
</rewrite>

The following shows the web.config setting for ASP.Net routing.

<addassembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>


… 

<httpModules>
       <addname="UrlRoutingModule"type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</httpModules>

In ASP.Net routing, we don't explicitly implement the HttpModule. Instead we create a class that implements the IRouteHandler interface and load this class in the Application_Start() event handler of the global.asax.

Here is our implementation of the IRouteHandler interface:

public class RouteHandler<T> : IRouteHandler where T : IHttpHandler, new()
       {
              public string VirtualPath { get; set; } 

              public RouteHandler(string virtualPath)
              {
                     this.VirtualPath = virtualPath;
              } 

              public IHttpHandler GetHttpHandler( RequestContext requestContext )
              {
                     IHttpHandler ret = null;
                     foreach ( var value in requestContext.RouteData.Values )
                     {
                           requestContext.HttpContext.Items[ value.Key ] = value.Value;
                     } 

                     ret = ( VirtualPath != null )
                           ? (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(
                           VirtualPath, typeof( T ) )
                           : new T(); 

                     return ret;
              }
       }

Most of this code is boiler plate to load an HttpHandler based on the mappings that you load up in the Application_Start() handler.

Our example site uses URL mappings defined in an XML file exported by a CMS. So we call a method in the Application_Start() handler in the global.asax that loads this XML file and creates a route for each entry. We do a bit of hand waving here by excluding the code for the CMS.Common.GetRoutes() method because it's probably unnecessary to go over the details parsing an XML.

void Application_Start(object sender, EventArgs e)
{
       RegisterRoutes(RouteTable.Routes);


public static void RegisterRoutes(RouteCollection routes)
{
       CMS.Route[]cmsRoutes = CMS.Common.GetRoutes(); 

       foreach (CMS.Route iCmsRoute in Booking cmsRoutes)
       {
              routes.Add(iCmsRoute.Name, new Route
              (
                      iCmsRoute.RequestPath,
                      new CustomRouteHandler(iCmsRoute.HandlerPath)
              ));
       }
}

Custom mapping URLs to HttpHandlers allows us to create websites with improved SEO, usability and security. The IIS7 URL Rewrite module and ASP.Net routing are two useful tools to map URLs. Each has its advantages and disadvantages, but can be used together to meet various URL mapping requirements.