Comment puis-je avoir des itinéraires en minuscules dans ASP.NET MVC?

145

Comment puis-je avoir des routes en minuscules, plus un trait de soulignement si possible dans ASP.NET MVC? Pour que /dinners/details/2j'appelle DinnersController.Details(2)et, si possible, /dinners/more_details/2appelle DinnersController.MoreDetails(2)?

Tout cela tout en utilisant des modèles comme {controller}/{action}/{id}.

J. Pablo Fernández
la source
J'ai fini par écrire tous mes itinéraires manuellement de toute façon pour diverses raisons et je pense qu'il est difficile d'éviter de le faire avec tout ce qui n'est pas seulement CRUD. Alors je les ai juste écrites en minuscules.
pupeno
Vous utilisez des formulaires Web ? Allez ici: msdn.microsoft.com/en-us/library/… . (Je convertis progressivement mon projet de formulaires Web en MVC et j'ai les deux dans le projet)
Jess
Je suis presque sûr que vous pouvez le faire par défaut
Je ne pense pas que ce soit important si vous tapez les itinéraires en minuscules ou en majuscules.

Réponses:

238

Avec System.Web.Routing 4.5, vous pouvez implémenter ce simple en définissant la propriété 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 }
        );
    }

En supposant également que vous faites cela pour des raisons de référencement, vous souhaitez rediriger les URL entrantes en minuscules (comme indiqué dans de nombreux liens de cet 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(".") == 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();
    }
 }
}
Aaron Sherman
la source
4
C'est de loin la chose la plus simple à faire en 4.0.
Paul Turner
1
J'aurais aimé que ce soit au sommet ... Presque cultivé le fret beaucoup de code!
akatakritos
2
Excellente réponse :-) Cette dernière partie SEO s'intègre parfaitement dans un module HTTP.
David Kirkland
1
@Aaron Sherman Où est censée aller la partie Application_BeginRequest? Cela me donne des erreurs quand c'est à l'intérieur de la classe publique RouteConfig et aussi quand c'est à l'extérieur de if.
Richard Mišenčík
1
@ richard-mišenčík l'ajouter au fichier Global.asax
ITmeze
44

Ces deux tutoriels m'ont aidé lorsque je voulais faire la même chose et bien fonctionner:

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

EDIT: Pour les projets avec des zones, vous devez modifier la méthode 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);
}
Derek Lawless
la source
9
Le lien GONeale a changé; L'URL est maintenant goneale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc
Daniel Liuzzi le
4
@Derek - Non, les tutoriels se décomposent lors de l'utilisation de Area. Après 3 jours à essayer TOUT ... j'ai trouvé une meilleure solution, il y a une bibliothèque appelée routage d'attributs. Résout le problème et rend la vie beaucoup plus facile. philliphaydon.com/2011/08/…
Phill
6
Pour mvc 4, il existe une meilleure solution en utilisant la propriété routes.LowercaseUrls = true; Plus d'infos sur dhuvelle.com/2012/11/tips-for-aspnet-mvc-4-lowercase-urls.html
Marc Cals
4
@GONeale Qu'est-il arrivé à votre URL? Le deuxième lien est mort maintenant! M'a fait rire cependant, le titre est "Le meilleur site de Gone Ale sur Internet"
Luke
2
@chteuchteu ce lien n'a pas fonctionné pour moi, mais celui-ci a fonctionné.
chue x
22

Si vous utilisez ASP.NET Core, vous devriez probablement jeter un œil à ceci :

Ajoutez la ligne suivante à la ConfigureServicesméthode de la Startupclasse.

services.AddRouting(options => options.LowercaseUrls = true);
Ebrahim Byagowi
la source
1
Idem pour Core 2.0. Aussi sur stackoverflow.com/a/45777372/195755
yzorg
21

Si vous utilisez UrlHelper pour générer le lien, vous pouvez simplement spécifier le nom de l'action et du contrôleur en minuscules:

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

Résultats dans: / media / photos / delete / 64 (même si mon contrôleur et mon action sont des cas pascal).

Mat
la source
16
Je pense que faire ce travail dans un emplacement central est la solution la plus simple et la plus standard. C'est tout aussi mauvais que le CSS en ligne. (Apparemment, 15 personnes utilisent le CSS en ligne).
The Muffin Man
Est-ce également vrai pour les serveurs Linux?
QMaster
15

J'ai trouvé cela au Coder Journal de Nick Berardi , mais il ne contenait pas d'informations sur la manière d'implémenter la LowercaseRouteclasse. Par conséquent, republier ici avec des informations supplémentaires.

Commencez par étendre la Routeclasse à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;
    }
}

Puis modifiez la RegisterRoutesméthode 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
    //);
}

J'aimerais cependant connaître un moyen d'utiliser les routes.MapRoute ...

John Oxley
la source
L'article de GONeale fournit une méthode d'extension afin que vous puissiez écrire routes.MapRouteLowercase(...ce qui est plus agréable que ce qui précède: goneale.wordpress.com/2008/12/19
Drew Noakes
1
Tout le blog de GONeale a disparu. Voici une autre entrée de blog avec un contenu similaire (et la même méthode d'extension). Il aborde cette situation dans le contexte de la réduction du contenu dupliqué.
patridge
11

Vous pouvez continuer à utiliser la syntaxe MapRoute en ajoutant cette classe comme extension à 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;
    }
}

Vous pouvez maintenant utiliser au démarrage de votre application "MapRouteLowerCase" au lieu 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
        );
    }
Markus Wolters
la source
Pour tous ceux qui lisent ceci, la LowercaseRouteclasse du premier extrait de code ci-dessus semble provenir de cette autre réponse
chue x
6

Cela a en fait deux réponses:

  1. Vous pouvez déjà le faire: le moteur d'itinéraire effectue une comparaison insensible à la casse. Si vous tapez une route en minuscules, elle sera acheminée vers le contrôleur et l'action appropriés.
  2. Si vous utilisez des contrôles qui génèrent des liens de route (ActionLink, RouteLink, etc.), ils produiront des liens à casse mixte, sauf si vous remplacez ce comportement par défaut.

Vous êtes seul pour les traits de soulignement, cependant ...

GalactiqueCowboy
la source
2

Pourriez-vous utiliser l'attribut ActionName?

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

 }

Je ne pense pas que le cas compte. More_Details, more_DETAILS, mOrE_DeTaILs dans l'URL vous amènent tous à la même action de contrôleur.

GuyIncognito
la source
Je n'ai pas essayé cela - vous permettra-t-il d'utiliser l'un ou l'autre? ("moredetails" ou "more_details")
GalacticCowboy
Pour faire un suivi, je l'ai essayé et il vous oblige à utiliser le nom spécifié, donc non, cela ne vous permettra pas de le gérer de toute façon. En outre, selon la façon dont vous avez construit votre action de contrôleur et votre vue, vous devrez peut-être spécifier le nom de la vue explicitement.
GalacticCowboy