Skip to content

Lowercase Route URL's in ASP.NET MVC

For anybody interesting in having ruby on rails style routes; or follow a lowercase URL pattern (mysite.com/member/profile/goneale) which most web 2.0 sites seem to be following, check out the below code cited and enhanced from this article (http://www.makiwa.com/index.php/2008/05/31/lowercase-mvc-route-urls/).

Firstly you will need a RouteExtensions.cs file, or named anything you like with the following (compatible as at ASP.NET MVC RC1):

using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace MyMvcApplication.App.Helpers
{
    public class LowercaseRoute : System.Web.Routing.Route
    {
        public LowercaseRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler) { }
        public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler) { }
        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler) { }
        public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            : base(url, defaults, constraints, dataTokens, routeHandler) { }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            VirtualPathData path = base.GetVirtualPath(requestContext, values);

            if (path != null)
                path.VirtualPath = path.VirtualPath.ToLowerInvariant();

            return path;
        }
    }

    public static class RouteCollectionExtensions
    {
        public static void MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults)
        {
            routes.MapRouteLowercase(name, url, defaults, null);
        }

        public static void MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults, object constraints)
        {
            if (routes == null)
                throw new ArgumentNullException("routes");

            if (url == null)
                throw new ArgumentNullException("url");

            var route = new LowercaseRoute(url, new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(defaults),
                Constraints = new RouteValueDictionary(constraints)
            };

            if (String.IsNullOrEmpty(name))
                routes.Add(route);
            else
                routes.Add(name, route);
        }
    }
}

Then a using reference in your Global.asax.cs file to the above class, and you’re all set to create a lowercase route.
You can see a below example of a lowercase route and anytime this route is called your URL will be lowercased.

            routes.MapRouteLowercase(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new {controller = "Home", action = "index", id = ""} // Parameter defaults
                );

and optionally if you are interested in converting any incoming URL’s to lowercase (manually typed by the user or called links) you can use this in your Application_BeginRequest() method (Remember, this is not needed for lowercase routes themselves, the code above will handle that):

        protected void Application_BeginRequest(Object sender, EventArgs e)
        {
            // If upper case letters are found in the URL, redirect to lower case URL.
            // Was receiving undesirable results here as my QueryString was also being converted to lowercase.
            // You may want this, but I did not.
            //if (Regex.IsMatch(HttpContext.Current.Request.Url.ToString(), @"[A-Z]") == true)
            //{
            //    string LowercaseURL = HttpContext.Current.Request.Url.ToString().ToLower();

            //    Response.Clear();
            //    Response.Status = "301 Moved Permanently";
            //    Response.AddHeader("Location", LowercaseURL);
            //    Response.End();
            //}

            // If upper case letters are found in the URL, redirect to lower case URL (keep querystring the same).
            string lowercaseURL = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
            if (Regex.IsMatch(lowercaseURL, @"[A-Z]"))
            {
                lowercaseURL = lowercaseURL.ToLower() + HttpContext.Current.Request.Url.Query;

                Response.Clear();
                Response.Status = "301 Moved Permanently";
                Response.AddHeader("Location", lowercaseURL);
                Response.End();
            }
        }
VN:F [1.9.1_1087]
Rating: 4.8/5 (8 votes cast)
VN:F [1.9.1_1087]
Rating: +1 (from 5 votes)
Lowercase Route URL's in ASP.NET MVC, 4.8 out of 5 based on 8 ratings
Bookmark and Share
kick it on DotNetKicks.com
Shout it

NOW, FOR A WORD FROM OUR SPONSORS

9 Comments

  1. Terry Donaghe

    Hi Graham. This may be a really lame question, but what plugin, etc are you using here to display your code? I will be setting up a wordpress.com coding blog in the near future and would like this functionality. Feel free to email me the answer and then delete this comment.

    Thanks!

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 31-Dec-08 at 7:22 am | Permalink
  2. grahamoneale

    Hi Terry,
    That’s fine. I might as well keep your comment, makes my blog look busy ;)
    It is one built in to wordpress I found. Simply encapsulate your code in tags like this:
    [sourcecode language='language-here']
    hello!
    [/sourcecode]
    More info and languages that can be used can be found here:
    http://support.wordpress.com/code/5/

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 31-Dec-08 at 9:13 am | Permalink
  3. First of all congratulation for such a great site. I learned a lot reading here today. I will make sure i visit this site more often so I can learn more.

    Make your long Urls shorter – Free Url redirection – Hide your affilate URLS

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 12-Jan-09 at 12:21 am | Permalink
  4. Graham O'Neale

    Thanks Josh!

    Really glad you’ve found it useful. I try to update it regularly, so feel free to drop by anytime. I also have an RSS feed you can use any feed reader to subscribe to if you like @ http://goneale.wordpress.com/feed.

    I’ll actually make a hyperlink for that right now!

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 12-Jan-09 at 8:56 am | Permalink
  5. if (Regex.IsMatch(lowercaseURL, @”[A-Z]“) == true)
    this is nor correct i believe. Cause we will never go into the if statement even if url is not lowercase. We are using our lowercaseurl for checking uppercase characters which will never be true.

    Something like this works fine:
    if (Regex.IsMatch(HttpContext.Current.Request.Url.AbsoluteUri, @”[A-Z]“) == true)

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 21-Feb-09 at 1:16 am | Permalink
  6. Graham O'Neale

    @Celik: You know what Celik, this code needs to be re-worked, what happened was I originally was running with this RegEx and a different if statement (I believe from here http://stackoverflow.com/questions/170900/how-can-i-avoid-duplicate-content-in-asp-net-mvc-due-to-case-insensitive-urls-and), but ran in to issues with my QueryString being converted to lowercase also, which was undesirable. So what I do now, for all requests is ToLower() the Url and append the QueryString in original casing.

    The old code was:
    // If upper case letters are found in the URL, redirect to lower case URL.
    if (Regex.IsMatch(HttpContext.Current.Request.Url.ToString(), @”[A-Z]“) == true)
    {
    string LowercaseURL = HttpContext.Current.Request.Url.ToString().ToLower();

    Response.Clear();
    Response.Status = “301 Moved Permanently”;
    Response.AddHeader(“Location”, LowercaseURL);
    Response.End();
    }

    You can see there that the entire URI including QS is converted to lower, so if you have a URL like localhost/my/page/list/2?CategoryID=40 you may want to keep CategoryID in this casing.
    I can’t recall where I needed my QS to remain in correct casing but it was a problem at the time.
    I will update the code sample with an actually useful Global.asax :) (This has been done!)

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 24-Feb-09 at 9:24 am | Permalink
  7. Thanks Graham for the code. It helped me resolve the case-insensitive duplicate content issue. But it caused another problem. When you visit the website without any page mentioned. It will show the name of the landing page in the URL. I tried to remove that landing page from the URL before redirecting, but it didn’t work. Please see if there is any fix for it.

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 29-Jul-09 at 3:29 pm | Permalink
  8. Pleun

    Great article, thank you so much :)

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 26-Jan-10 at 12:38 am | Permalink
  9. Thanks for the solution, but this doesn’t work for Area’s. I found a solution for area’s here http://www.google.com/codesearch/p?hl=en#RvvkzXvPN5c/src/BetaBlog/Web/Routing/RouteCollectionExtensions.cs&q=RouteCollectionExtensions%20package:http://betablog\.googlecode\.com&sa=N&cd=1&ct=rc

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
    Posted on 27-Jun-10 at 9:45 pm | Permalink

One Trackback/Pingback

  1. [...] post is based off 2 other blog posts I found which helped me come up with my solution with some slight modifications on their [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*
My name is Graham O'Neale and I'm a software architect from Gold Coast, Australia. I am an overtime thinker, full time coder and awake part time in the real world. I have a keen interest in software development, particularly in the realm of programming (C#, ASP.NET, ASP.NET MVC, LINQ (2 SQL), Entity Framework, Silverlight, Blend, WCF, WPF) and a keen interest in the cutting edge and innovation. I have a new found love for design patterns, ALT.NET practices and well crafted software architecture. The purpose of this blog is to express any thoughts, findings, tips and gripes along my travels in the wonderful world of coding and technology...