Come posso avere percorsi in minuscolo in ASP.NET MVC?


145

Come posso avere percorsi minuscoli, oltre alla sottolineatura, se possibile, in ASP.NET MVC? In modo che avrei /dinners/details/2chiamare DinnersController.Details(2)e, se possibile, /dinners/more_details/2chiamare DinnersController.MoreDetails(2)?

Tutto questo pur usando modelli come {controller}/{action}/{id}.


Ho finito per scrivere tutti i miei percorsi manualmente comunque per vari motivi e penso che sia difficile evitare di farlo con qualsiasi cosa che non sia solo CRUD. Quindi li ho appena scritti in minuscolo.
pupeno,

Utilizzando i moduli Web ? Vai qui: msdn.microsoft.com/en-us/library/… . (Sto gradualmente convertendo il mio progetto da moduli Web a MVC e li ho entrambi nel progetto)
Jess

Sono abbastanza sicuro che puoi farlo come predefinito

non penso che importi se inserisci i percorsi in lettere minuscole o maiuscole.

Risposte:


238

Con System.Web.Routing 4.5 puoi implementarlo direttamente impostando la proprietà LowercaseUrls di 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 }
        );
    }

Supponendo anche che tu lo stia facendo per ragioni SEO, vuoi reindirizzare gli URL in entrata in minuscolo (come detto in molti dei link di questo articolo).

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, @"[A-Z]"))
     {
      //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();
    }
 }
}

4
Questa è di gran lunga la cosa più semplice da fare in 4.0.
Paul Turner,

1
Vorrei che questo fosse in cima ... Quasi un sacco di codice di culto!
Akatakritos,

2
Ottima risposta :-) L'ultima parte SEO si adatta perfettamente a un modulo HTTP.
David Kirkland,

1
@Aaron Sherman Dov'è supposta la parte Application_BeginRequest? Mi dà errori quando si trova all'interno della classe pubblica RouteConfig e anche quando è all'esterno di if.
Richard Mišenčík,

1
@ richard-mišenčík aggiungilo al file Global.asax
ITmeze

44

Questi due tutorial mi hanno aiutato quando volevo fare la stessa cosa e lavorare bene:

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

EDIT: per i progetti con aree, è necessario modificare il metodo 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);
}

9
Il link GONeale è cambiato; L'URL è ora goneale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc
Daniel Liuzzi

4
@Derek - No, i tutorial si interrompono quando si utilizzano le aree. Dopo 3 giorni di prove TUTTO ... Ho trovato una soluzione migliore, c'è una libreria chiamata Attribute Routing. Risolve il problema e semplifica la vita. philliphaydon.com/2011/08/…
Phill

6
Per mvc 4 esiste una soluzione migliore utilizzando le rotte di proprietà.LowercaseUrls = true; Maggiori informazioni su dhuvelle.com/2012/11/tips-for-aspnet-mvc-4-lowercase-urls.html
Marc Cals

4
@GONeale Cos'è successo al tuo URL? Il secondo link è morto ora! Mi ha fatto ridere però, il titolo è "Il miglior sito di Ale andato su Internet"
Luca

2
@chteuchteu quel link non ha funzionato per me, ma questo ha funzionato.
chue x


21

Se stai usando UrlHelper per generare il link, puoi semplicemente specificare il nome dell'azione e del controller come minuscolo:

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

Risultati in: / media / photos / delete / 64 (anche se il mio controller e l'azione sono un caso pasquale).


16
Penso che fare questo lavoro in una posizione centrale sia la soluzione più semplice e più standard. Questo è altrettanto grave del CSS in linea. (Apparentemente 15 persone usano CSS in linea).
The Muffin Man,

È vero anche per i server Linux?
QMaster

15

L'ho trovato sul Coder Journal di Nick Berardi , ma non aveva informazioni su come implementare la LowercaseRouteclasse. Quindi ripubblicare qui con ulteriori informazioni.

Per prima cosa estendi la Routeclasse aLowercaseRoute

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

Quindi modificare il RegisterRoutesmetodo di 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
    //);
}

Vorrei tuttavia sapere un modo per utilizzare i percorsi.MapRoute ...


L'articolo di GONeale fornisce un metodo di estensione in modo da poter scrivere routes.MapRouteLowercase(...che è più bello di quanto sopra: goneale.wordpress.com/2008/12/19/…
Drew Noakes

1
L'intero blog di GONeale è scomparso. Ecco un altro post di blog con contenuti simili (e lo stesso metodo di estensione). Affronta questa situazione nel contesto della riduzione di contenuti duplicati.
Patridge,

11

Puoi continuare a utilizzare la sintassi di MapRoute aggiungendo questa classe come estensione 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;
    }
}

Ora è possibile utilizzare all'avvio dell'applicazione "MapRouteLowerCase" anziché "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
        );
    }

Per chiunque legga questo, la LowercaseRouteclasse nel primo frammento di codice sopra sembra provenire da questa altra risposta
chue x

6

Questo in realtà ha due risposte:

  1. Puoi già farlo: il motore di route esegue un confronto senza distinzione tra maiuscole e minuscole. Se si digita una route minuscola, verrà indirizzata al controller e all'azione appropriati.
  2. Se si utilizzano controlli che generano collegamenti di instradamento (ActionLink, RouteLink, ecc.), Genereranno collegamenti in maiuscolo a meno che non si ignori questo comportamento predefinito.

Sei da solo per i caratteri di sottolineatura, anche se ...


2

Potresti usare l'attributo ActionName?

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

 }

Non credo che il caso contenga. More_Details, more_DETAILS, mOrE_DeTaILs nell'URL portano tutti alla stessa azione del controller.


Non l'ho provato - ti permetterà di usare uno dei due? ("moredetails" o "more_details")
GalacticCowboy,

Per seguire, l'ho provato e richiede di utilizzare il nome specificato, quindi no, non ti permetterà di gestirlo in entrambi i modi. Inoltre, a seconda del modo in cui è stata creata l'azione e la vista del controller, potrebbe essere necessario specificare esplicitamente il nome della vista.
GalacticCowboy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.