Как я могу иметь строчные маршруты в ASP.NET MVC?

145

Как я могу иметь строчные, плюс подчеркивание, если это возможно, маршруты в ASP.NET MVC? Чтобы мне /dinners/details/2позвонили DinnersController.Details(2)и, если возможно, /dinners/more_details/2позвонили DinnersController.MoreDetails(2)?

Все это при использовании шаблонов, как {controller}/{action}/{id}.

J. Pablo Fernández
источник
Я все-таки написал все свои маршруты вручную по разным причинам, и я думаю, что трудно избежать этого с чем-то, кроме CRUD. Поэтому я просто написал их в нижнем регистре.
Пупено
Использование веб-форм ? Зайдите сюда: 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
@ Аарон Шерман Куда предполагается добавить часть 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/

РЕДАКТИРОВАТЬ: Для проектов с областями, вам необходимо изменить метод 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-адрес теперь ушел: annale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc
Даниэль Лиуцци,
4
@Derek - Нет, учебники ломаются при использовании Area. После 3 дней попыток ВСЕ ... Я нашел лучшее решение, есть библиотека под названием Attribute Routing. Решает проблему и делает жизнь намного проще. philliphaydon.com/2011/08/…
Phill
6
Для mvc 4 есть лучшее решение, использующее свойство route.LowercaseUrls = true; Больше информации на dhuvelle.com/2012/11/tips-for-aspnet-mvc-4-lowercase-urls.html
Марк Калс,
4
@ GONeale Что случилось с вашим URL? Вторая ссылка мертва! Хотя заставил меня смеяться, заголовок «Лучший Gone Ale сайт в Интернете»
Люк
2
@chteuchteu эта ссылка не сработала для меня, но эта сработала .
че х
22

Если вам довелось использовать ASP.NET Core, вам, вероятно, стоит взглянуть на это :

Добавьте следующую строку в ConfigureServicesметод Startupкласса.

services.AddRouting(options => options.LowercaseUrls = true);
оборота Эбрахим Бягови
источник
1
То же самое для Core 2.0. Также на stackoverflow.com/a/45777372/195755
yzorg
21

Если вы используете UrlHelper для генерации ссылки, вы можете просто указать имя действия и контроллер в нижнем регистре:

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

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

Matt
источник
16
Я думаю, что выполнение этой работы в одном месте - самое простое и стандартное решение. Это так же плохо, как встроенный CSS. (Видимо 15 человек используют встроенный CSS).
Человек
Это правда и для серверов Linux?
QMaster
15

Я нашел это в журнале Coder Journal Ника Берарди , но там не было информации о том, как реализовать 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
    //);
}

Однако я хотел бы знать, как использовать маршруты. КартаRoute ...

Джон Оксли
источник
Статья GONeale предоставляет метод расширения, так что вы можете написать, routes.MapRouteLowercase(...что лучше, чем указано выше: didale.wordpress.com/2008/12/19/…
Drew Noakes
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
        );
    }
Markus Wolters
источник
Для любого, кто читает это, LowercaseRouteкласс в первом фрагменте кода выше, кажется, прибывает из этого другого ответа
chue x
6

На самом деле это имеет два ответа:

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

Вы сами по себе для подчеркивания, хотя ...

GalacticCowboy
источник
2

Не могли бы вы использовать атрибут ActionName?

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

 }

Я не думаю, что дело имеет значение. More_Details, more_DETAILS, mOrE_DeTaILs в URL ведут вас к одному и тому же действию контроллера.

GuyIncognito
источник
Я не пробовал это - это позволит вам использовать любой из них? («moredetails» или «more_details»)
GalacticCowboy
Чтобы продолжить, я попробовал это, и оно требует от вас использования указанного имени, так что нет, оно не позволит вам справиться с этим в любом случае. Кроме того, в зависимости от того, как вы сконструировали действие и представление вашего контроллера, вам может понадобиться явно указать имя представления.
GalacticCowboy