ASP.NET Module for 301 Redirects

Posted by Tom on 2013-02-14 22:33

Part of the transition from the old and complicated blog system to the new and idiot-simple one would mean changing some URLs. Obviously I'm reluctant to lose all of the precious, precious PageRank™ that I don't have, so some 301s were in order.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Web;

namespace Colourblind.RedirectModule
{
    public class RedirectModule : IHttpModule
    {
        public void Dispose() { }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
                return;

            string requestPath = context.Request.CurrentExecutionFilePath.ToLower();
            if (RedirectConfig.Redirects.ContainsKey(requestPath))
            {
                HttpResponse response = context.Response;
                response.Clear();
                response.Status = "301 Moved Permanently";
                response.AddHeader("Location", RedirectConfig.Redirects[requestPath]);
                response.End();
            }
        }
    }

    public class RedirectConfig : ConfigurationSection
    {
        public static IDictionary<string, string> Redirects
        {
            get;
            private set;
        }

        static RedirectConfig()
        {
            Redirects = new Dictionary<string, string>();
            RedirectConfig config = (RedirectConfig)ConfigurationManager.GetSection("redirects");
            foreach (Redirect redirect in config.Instances)
                Redirects.Add(redirect.From.ToLower(), redirect.To);
        }

        [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
        public RedirectList Instances
        {
            get { return (RedirectList)this[""]; }
            set { this[""] = value; }
        }
    }

    public class RedirectList : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new Redirect();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((Redirect)element).From;
        }
    }

    public class Redirect : ConfigurationElement
    {
        [ConfigurationProperty("from", IsKey = true, IsRequired = true)]
        public string From
        {
            get { return (string)this["from"]; }
            set { this["from"] = value; }
        }

        [ConfigurationProperty("to", IsRequired = true)]
        public string To
        {
            get { return (string)this["to"]; }
            set { this["to"] = value; }
        }
    }
}

You'll need to set up the new config section by first adding the following to the <configSection> element:

<configSections>
    <!-- OTHER SECTIONS -->
    <section name="redirects" type="Colourblind.RedirectModule.RedirectConfig, Colourblind.RedirectModule" />
</configSections>

And then dump the following somewhere else in the .config file

<redirects>
    <add from="/post/doin-the-legacy-mambo.aspx" to="/Doin-the-legacy-mambo.html" />
    <add from="/post/natas-ctf---levels-11-to-16.aspx" to="/Natas-CTF---Levels-11-to-16.html" />
</redirects>

Make sure the incoming URLs which you want to move are in lower-case. When those URLs are hit the client will be sent a 301, or permanent, redirect to the specified URL.

Finally, you'll need to add the module to the relevant config section. This will depend on which version of IIS you're running, and how it's set up. To be honest, I just hedge my bets and stuff it in both.

And you're good to go!