Monday, June 21, 2010

ASP.NET - Redirect Module

As developers we are all intimately aware of the implications of changing the URL of existing content in our applications which have already been indexed by search engines and users. It is never enough to simply change a URL and update the links because old pages, bookmarks, or search results may still point to the original URL. That being so, it is imperative to add an appropriate HTTP 301 redirect for the original URL when modifying the location of existing content.

In Apache-based web servers adding a 301 Redirect is as simple as adding a .htaccess file with the appropriate mod_rewrite instructions; unfortunately, this simplicity of configuration is unavailable on an IIS server and when an application requires a redirect be added we are forced to either edit the IIS settings explicitly or add the redirect to the application’s code. Ultimately, the former solution is ideal because it allows IIS to bypass the ASP.NET request engine and immediately serve the redirect, but for many of us, editing the IIS configuration is not an option due to the use of shared hosting environments. In ASP.NET code, issuing a 301 Redirect in your Global.asax looks something like this:

protected void Application_BeginRequest(object sender, EventArgs e) {
    var oldUrl = new Uri("http://www.website.com/path/to/content");
    var newUrl = new Uri("http://www.website.com/new/path/to/content");

    if(string.Equals(Request.Url.AbsolutePath, oldUrl.AbsolutePath, StringComparison.OrdinalIgnoreCase)) {
        Response.StatusCode = 301;
        Response.Status = "301 Moved Permanently";
        Response.RedirectLocation = newUrl;
        Response.End();
    }
}

While searching for a more maintainable solution I came across a blog post by Scott Hanselman which directed me to a HttpModule written by Fritz Onion. The module developed is elegant and effective because it uses a special section in the website's configuration file to define each redirect, thus minimizing maintenance challenges associated with adding and removing individual redirects. That being said, Fritz's solution is dated and uses some obsolete code so I have gone ahead and modernized it a bit. In addition to the original feature set, I have added support for matching the entire request URL.

Issuing redirects with the module is as easy as registering and adding the <redirections /> configuration section and then declaring each redirect rule.

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="redirections" type="RedirectModule.Configuration.RedirectionsSection, RedirectModule"/>
  </configSections>
  <redirections>
    <add targetUrl="^http://website.com" destinationUrl="http://www.website.com" ignoreCase="true" />
    <add targetUrl="^~/FalseTarget.aspx" destinationUrl="~/RealTarget.aspx" ignoreCase="true"/>
    <add targetUrl="^~/2ndFalseTarget.aspx" destinationUrl="~/RealTarget.aspx" permanent="true"/>
    <add targetUrl="^~/(Author|Category|Tool)([A-Za-z0\d]{8}-?[A-Za-z\d]{4}-?[A-Za-z\d]{4}-?[A-Za-z\d]{4}-?[A-Za-z\d]{12}).aspx$" destinationUrl="~/Pages/$1.aspx?$1=$2"/>
    <add targetUrl="^~/SomeDir/(.*).aspx\??(.*)" destinationUrl="~/Pages/$1/Default.aspx?$2"/>
  </redirections>
  <system.web>
    <httpModules>
      <add name="RedirectModule" type="RedirectModule.RedirectModule"/>
    </httpModules>
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="RedirectModule" type="RedirectModule.RedirectModule, RedirectModule"/>
    </modules>
  </system.webServer>
</configuration>

Sources

Update: 03/26/12

I've made a few minor tweaks in the module's code. The library now targets .NET 2.0 for maximum compatibility and the sample project works much better out of the box.

7 comments:

Ben Foster said...

Hi Nathan,
The download link doesn't work. Is there an updated link?

Nathan Taylor said...

@BenFoster I have updated the link to a working URL. Sorry about that!

JustSomeNewKid said...

section name="redirections" type="RedirectModule.RedirectionsSection, RedirectModule"

should actually be

section name="redirections" type="RedirectModule.Configuration.RedirectionsSection, RedirectModule"

btw

Nathan Taylor said...

@JustSomeNewKid

Thanks for catching that!

JustSomeNewKid said...

@Nathan Taylor

np!, ilSpy told me the datatypes in the binaries. It works like a charm though, the regular expression is a life saver... especially when you have 100's of URL's to redirect!

Kaushik Halvadia said...

In this article, FalseTarget.aspx page redirect user to RealTarget.aspx in such a way whats means of (Author|Category|Tool)([A-Za-z0\d]{8}-?[A-Za-z\d]{4}-?[A-Za-z\d]{4}-?[A-Za-z\d]{4}-?[A-Za-z\d]{12}).aspx$




In short, I want to know regexp used in url rewritting.





Help me!!!

Nathan Taylor said...

@Kaushik Halvadia the code in that rule effectively says that the redirect module should intercept any URL matching "Author/<GUID>.aspx", "Category/<GUID>.aspx", or "Tool/<GUID>.aspx" and redirect it to Author.aspx?author=<GUID>, Category.aspx?category=<GUID>, or Tool.aspx?tool=<GUID> respectively.