How can I have lowercase routes in ASP.NET MVC?

How can I have lowercase, plus underscore if possible, routes in ASP.NET MVC? So that I would have /dinners/details/2 call DinnersController.Details(2) and, if possible, /dinners/more_details/2 call DinnersController.MoreDetails(2)?

All this while still using patterns like {controller}/{action}/{id}.


Solution 1:

With System.Web.Routing 4.5 you may implement this straightforward by setting LowercaseUrls property of RouteCollection:

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.LowercaseUrls = true;

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

Also assuming you are doing this for SEO reasons you want to redirect incoming urls to lowercase (as said in many of the links off this article).

protected void Application_BeginRequest(object sender, EventArgs e)
    {
        //You don't want to redirect on posts, or images/css/js
        bool isGet = HttpContext.Current.Request.RequestType.ToLowerInvariant().Contains("get");
        if (isGet && !HttpContext.Current.Request.Url.AbsolutePath.Contains("."))
        {
            //You don't want to modify URL encoded chars (ex.: %C3%8D that's code to Í accented I) to lowercase, than you need do decode the URL
            string urlLowercase = Request.Url.Scheme + "://" + HttpUtility.UrlDecode(HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
            //You want to consider accented chars in uppercase check
            if (Regex.IsMatch(urlLowercase, @"[A-Z]") || Regex.IsMatch(urlLowercase, @"[ÀÈÌÒÙÁÉÍÓÚÂÊÎÔÛÃÕÄËÏÖÜÝÑ]"))
            {
                //You don't want to change casing on query strings
                urlLowercase = urlLowercase.ToLower() + HttpContext.Current.Request.Url.Query;

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

Solution 2:

These two tutorials helped when I wanted to do the same thing and work well:

http://www.coderjournal.com/2008/03/force-mvc-route-url-lowercase/ http://goneale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc/

EDIT: For projects with areas, you need to modify the GetVirtualPath() method:

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
  var lowerCaseValues = new RouteValueDictionary();

  foreach (var v in values)
  {
    switch (v.Key.ToUpperInvariant())
    {
      case "ACTION":
      case "AREA":
      case "CONTROLLER":
        lowerCaseValues.Add(v.Key, ((string)v.Value).ToLowerInvariant());
        break;
      default:
        lowerCaseValues.Add(v.Key.ToLowerInvariant(), v.Value);
        break;
    }
  }
  return base.GetVirtualPath(requestContext, lowerCaseValues);
}

Solution 3:

If you happened to use ASP.NET Core, you probably should have a look at this:

Add the following line to the ConfigureServices method of the Startup class.

services.AddRouting(options => options.LowercaseUrls = true);

Solution 4:

If you are using the UrlHelper to generate the link, you can simply specify the name of the action and controller as lowercase:

itemDelete.NavigateUrl = Url.Action("delete", "photos", new { key = item.Key });

Results in: /media/photos/delete/64 (even though my controller and action are pascal case).