¿Cómo puedo tener rutas en minúsculas en ASP.NET MVC?

¿Cómo puedo tener rutas en minúsculas, además de subrayado si es posible, en ASP.NET MVC? Para que yo tenga /dinners/details/2 llame a DinnersController.Details(2) y, si es posible, /dinners/more_details/2 llame a DinnersController.MoreDetails(2) ?

Todo esto sin dejar de usar patrones como {controller}/{action}/{id} .

Con System.Web.Routing 4.5 puede implementar esto de manera directa estableciendo la propiedad LowercaseUrls de 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 } ); } 

También asumiendo que estás haciendo esto por razones de SEO, quieres redirigir las URL entrantes a minúsculas (como se dice en muchos de los enlaces de este artículo).

 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(".") == false) { string lowercaseURL = (Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath); if (Regex.IsMatch(lowercaseURL, @"[AZ]")) { //You don't want to change casing on query strings lowercaseURL = lowercaseURL.ToLower() + HttpContext.Current.Request.Url.Query; Response.Clear(); Response.Status = "301 Moved Permanently"; Response.AddHeader("Location", lowercaseURL); Response.End(); } } } 

Estos dos tutoriales me ayudaron cuando quería hacer lo mismo y trabajar bien:

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

EDITAR: para proyectos con áreas, debe modificar el método GetVirtualPath ():

 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); } 

Si está utilizando UrlHelper para generar el enlace, puede simplemente especificar el nombre de la acción y el controlador como minúsculas:

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

Resultados en: / media / photos / delete / 64 (aunque mi controlador y acción son pascal).

Encontré esto en el Coder Journal de Nick Berardi , pero no tenía información sobre cómo implementar la clase LowercaseRoute . Por lo tanto, volver a publicar aquí con información adicional.

Primero amplíe la clase Route a Route en LowercaseRoute

 public class LowercaseRoute : 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; } } 

A continuación, modifique el método RegisterRoutes de Global.asax.cs

 public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.Add(new LowercaseRoute("{controller}/{action}/{id}", new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }), new MvcRouteHandler())); //routes.MapRoute( // "Default", // Route name // "{controller}/{action}/{id}", // URL with parameters // new { controller = "Home", action = "Index", id = "" } // Parameter defaults //); } 

Sin embargo, me gustaría saber una forma de usar routes.MapRoute …

Puede continuar usando la syntax de MapRoute agregando esta clase como una extensión a RouteCollection:

 public static class RouteCollectionExtension { public static Route MapRouteLowerCase(this RouteCollection routes, string name, string url, object defaults) { return routes.MapRouteLowerCase(name, url, defaults, null); } public static Route MapRouteLowerCase(this RouteCollection routes, string name, string url, object defaults, object constraints) { Route route = new LowercaseRoute(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints) }; routes.Add(name, route); return route; } } 

Ahora puede usar en el inicio de su aplicación “MapRouteLowerCase” en lugar de “MapRoute”:

  public void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); // Url shortcuts routes.MapRouteLowerCase("Home", "", new { controller = "Home", action = "Index" }); routes.MapRouteLowerCase("Login", "login", new { controller = "Account", action = "Login" }); routes.MapRouteLowerCase("Logout", "logout", new { controller = "Account", action = "Logout" }); routes.MapRouteLowerCase( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults ); } 

Si usó ASP.NET Core, probablemente debería echar un vistazo a esto :

Agregue la siguiente línea al método ConfigureServices de la clase de Startup .

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

Esto en realidad tiene dos respuestas:

  1. Ya puede hacer esto: el motor de ruta hace una comparación insensible a mayúsculas y minúsculas. Si escribe una ruta en minúscula, se enrutará al controlador y a la acción apropiados.
  2. Si está utilizando controles que generan enlaces de ruta (ActionLink, RouteLink, etc.) producirán enlaces de casos mezclados a menos que anule este comportamiento predeterminado.

Sin embargo, estás solo por los guiones bajos …

¿Podría usar el atributo ActionName?

  [ActionName("more_details")] public ActionResult MoreDetails(int? page) { } 

No creo que el caso importe. More_Details, more_DETAILS, mOrE_DeTaILs en la URL lo llevan a la misma Acción del controlador.