Як я можу мати малі маршрути в ASP.NET MVC?


145

Як я можу мати маршрути в ASP.NET MVC з малих літер та підкреслення, якщо це можливо? Так що я б /dinners/details/2зателефонував DinnersController.Details(2)і, якщо можливо, /dinners/more_details/2зателефонував DinnersController.MoreDetails(2)?

Все це при використанні подібних моделей {controller}/{action}/{id}.


Я все-таки писав усі свої маршрути вручну з різних причин, і я думаю, що важко уникнути цього з усім, що є не лише CRUD. Тому я просто написав їх малими літерами.
pupeno

Використовуєте веб-форми ? Перейти тут: msdn.microsoft.com/en-us/library / ... . (Я поступово перетворюю свій проект з веб-форм на MVC і маю і те, і інше в проекті)
Джесс

Я впевнений, що ви можете це зробити за замовчуванням

Я не думаю, що це має значення, якщо ви вводите маршрути в нижньому або верхньому регістрі.

Відповіді:


238

За допомогою System.Web.Routing 4.5 ви можете реалізувати це прямо, встановивши властивість LowercaseUrls 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 }
        );
    }

Також припускаючи, що ви робите це з причин SEO, ви хочете перенаправляти вхідні URL на малі регістри (як сказано у багатьох посиланнях, викладених у цій статті).

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
Це, безумовно, найпростіше зробити в 4.0.
Пол Тернер

1
Бажайте, щоб це було на вершині ... Майже вантажовипускає багато коду!
акатакрітос

2
Чудова відповідь :-) Остання частина SEO добре вписується в модуль HTTP.
Девід Кіркленд

1
@Aaron Sherman Куди планується поїхати частина Application_BeginRequest? Це дає мені помилки, коли це всередині загальнодоступного RouteConfig, а також коли його поза if.
Річард Мішенчик

1
@ richard-mišenčík додати його у файл Global.asax
ITmeze

44

Ці два навчальні посібники допомагали, коли я хотів зробити те саме і працювати добре:

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

EDIT: Для проектів з областями вам потрібно змінити метод 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
Посилання GONeale змінилося; URL-адреса зникла.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc
Даніель

4
@Derek - Ні, підручники розбиваються під час використання області. Після 3-х днів спроб ВСЕ ... Я знайшов краще рішення, є бібліотека під назвою Маршрутизація атрибутів. Вирішує проблему і значно полегшує життя. philliphaydon.com/2011/08/…
Phill

6
Для mvc 4 є краще рішення з використанням властивостей route.LowercaseUrls = true; Більше інформації про dhuvelle.com/2012/11/tips-for-aspnet-mvc-4-lowercase-urls.html
Marc Cals

4
@GONeale Що сталося з вашою URL-адресою? Друга посилання зараз мертва! Хоч я змусив мене сміятися, назва "The Best Gone Ale Site в Інтернеті"
Лука

2
@chteuchteu це посилання для мене не працювало, але це все- таки.
chue x


21

Якщо ви використовуєте UrlHelper для створення посилання, ви можете просто вказати ім'я дії та контролера як малі літери:

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

Результати в: / media / photos / delete / 64 (навіть якщо мій контролер і дія є випадковою справою).


16
Я думаю, що ця робота в одному центральному місці є найпростішим і найстандартнішим рішенням. Це так само погано, як і вбудований CSS. (Мабуть, 15 людей використовують вбудований CSS).
Людина-булочка

Це правда і для серверів Linux?
QMaster

15

Я знайшов це в журналі Coder Ніка Берарді , але в ньому не було інформації про те, як реалізуватиLowercaseRoute клас. Отже, повторне розміщення тут із додатковою інформацією.

Спочатку подовжте Routeклас на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;
    }
}

Потім змініть RegisterRoutesметод 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
    //);
}

Однак я хотів би знати спосіб використання маршрутів.


Стаття GONeale пропонує метод розширення, щоб ви могли написати, routes.MapRouteLowercase(...що приємніше, ніж вище: goneale.wordpress.com/2008/12/19/…
Дрю

1
Весь блог GONeale зник. Ось ще одна запис у блозі з подібним вмістом (і тим же методом розширення). Він вирішує цю ситуацію в контексті скорочення дублюючого вмісту.
патрон

11

Ви можете продовжити використовувати синтаксис MapRoute, додавши цей клас як розширення до 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;
    }
}

Тепер ви можете використовувати в запуску програми "MapRouteLowerCase" замість "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
        );
    }

Для всіх, хто читає це, LowercaseRouteклас у першому фрагменті коду вище, схоже, походить з цієї іншої відповіді
chue x

6

На це насправді є дві відповіді:

  1. Ви вже можете це зробити: двигун маршруту робить порівняння, що не враховує регістр. Якщо ви введете нижній регістр маршруту, він буде спрямований на відповідний контролер та дію.
  2. Якщо ви використовуєте елементи керування, які генерують маршрутні посилання (ActionLink, RouteLink тощо), вони створюватимуть змішані регістри, якщо ви не перекриєте цю поведінку за замовчуванням.

Ти сам наголошуєш на підкресленнях, хоча ...


2

Чи можете ви використовувати атрибут ActionName?

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

 }

Я не думаю, що справа має значення. More_Details, more_DETAILS, mOrE_DeTaILs в URL-адресі все приводять вас до однієї дії контролера.


Я не пробував цього - чи дозволить вам використовувати будь-який? ("moredetails" або "more_details")
GalacticCowboy

Щоб продовжити, я спробував це, і вам потрібно використовувати вказане ім'я, так що ні, це не дозволить вам впоратися з цим будь-яким способом. Крім того, залежно від того, як ви сконструювали дію та перегляд контролера, можливо, вам буде потрібно чітко вказати ім'я перегляду.
GalacticCowboy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.