title: How to correctly canonicalize a URL in an ASP.NET MVC application?
body: <p>I'm trying to find a good general purpose way to canonicalize urls in an ASP.NET MVC 2 application. Here's what I've come up with so far:</p>
  
  <pre><code>// Using an authorization filter because it is executed earlier than other filters
  public class CanonicalizeAttribute : AuthorizeAttribute
  {
      public bool ForceLowerCase { get;set; }
  
      public CanonicalizeAttribute()
          : base()
      {
          ForceLowerCase = true;
      }
  
      public override void OnAuthorization(AuthorizationContext filterContext)
      {
          RouteValueDictionary values = ExtractRouteValues(filterContext);
          string canonicalUrl = new UrlHelper(filterContext.RequestContext).RouteUrl(values);
          if (ForceLowerCase)
              canonicalUrl = canonicalUrl.ToLower();
  
          if (filterContext.HttpContext.Request.Url.PathAndQuery != canonicalUrl)
              filterContext.Result = new PermanentRedirectResult(canonicalUrl);
      }
  
      private static RouteValueDictionary ExtractRouteValues(AuthorizationContext filterContext)
      {
          var values = filterContext.RouteData.Values.Union(filterContext.RouteData.DataTokens).ToDictionary(x =&gt; x.Key, x =&gt; x.Value);
          var queryString = filterContext.HttpContext.Request.QueryString;
          foreach (string key in queryString.Keys)
          {
              if (!values.ContainsKey(key))
                  values.Add(key, queryString[key]);
          }
          return new RouteValueDictionary(values);
      }
  }
  
  // Redirect result that uses permanent (301) redirect
  public class PermanentRedirectResult : RedirectResult
  {
      public PermanentRedirectResult(string url) : base(url) { }
  
      public override void ExecuteResult(ControllerContext context)
      {
          context.HttpContext.Response.RedirectPermanent(this.Url);
      }
  }
  </code></pre>
  
  <p>Now I can mark up my controllers like this:</p>
  
  <pre><code>[Canonicalize]
  public class HomeController : Controller { /* ... */ }
  </code></pre>
  
  <p>This all appears to work fairly well, but I have the following concerns:</p>
  
  <ol>
  <li><p>I still have to add the <code>CanonicalizeAttribute</code> to every controller (or action method) I want canonicalized, when it's hard to think of a situation where I won't want this behaviour. It seems like there should be a way to get this behaviour site-wide, rather than one controller at a time.</p></li>
  <li><p>The fact that I'm implementing the 'force to lower-case' rule in the filter seems wrong. Surely it would be better to somehow role this up into the route url logic, but I can't think of a way to do this in my routing configuration. I thought of adding <code>@"[a-z]*"</code> constraints to the controller and action parameters (as well as any other string route parameters), but I think this will cause the routes to not be matched. Also, because the lower-case rule isn't being applied at the route level, it's possible to generate links in my pages that have upper-case letters in them, which seems pretty bad.</p></li>
  </ol>
  
  <p>Is there something obvious I'm overlooking here?</p>