如何在ASP.NET MVC中使用小写路由?

155

我该如何在ASP.NET MVC中使用小写、下划线(如果可能的话)的路由?这样我就可以使/dinners/details/2调用 DinnersController.Details(2),并且如果可能的话,/dinners/more_details/2 调用 DinnersController.MoreDetails(2)

同时仍然使用像 {controller}/{action}/{id} 这样的模式。


最终,由于各种原因,我还是手动编写了所有的路由,我认为对于任何不仅仅是CRUD的东西,很难避免这样做。所以我只是用小写字母编写它们。 - pupeno
使用 Web Forms 吗?请访问此链接:https://msdn.microsoft.com/zh-cn/library/cc668177.aspx?f=255&MSPPError=-2147217396。(我正在逐步将我的项目从 Web Forms 转换为 MVC,并且在项目中同时存在两者) - Jess
我相信你可以将其设置为默认值。 - user12053089
我认为无论您在路由中输入小写或大写字母都没有关系。 - user12053089
8个回答

254

使用System.Web.Routing 4.5,您可以通过设置RouteCollection的LowercaseUrls属性来轻松实现此操作:

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("."))
        {
            //You don't want to modify URL encoded chars (ex.: %C3%8D that's code to Í accented I) to lowercase, than you need do decode the URL
            string urlLowercase = Request.Url.Scheme + "://" + HttpUtility.UrlDecode(HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.Url.AbsolutePath);
            //You want to consider accented chars in uppercase check
            if (Regex.IsMatch(urlLowercase, @"[A-Z]") || Regex.IsMatch(urlLowercase, @"[ÀÈÌÒÙÁÉÍÓÚÂÊÎÔÛÃÕÄËÏÖÜÝÑ]"))
            {
                //You don't want to change casing on query strings
                urlLowercase = urlLowercase.ToLower() + HttpContext.Current.Request.Url.Query;

                Response.Clear();
                Response.Status = "301 Moved Permanently";
                Response.AddHeader("Location", urlLowercase);
                Response.End();
            }
        }
    }

5
在4.0版本中,这绝对是最简单的事情。 - Paul Turner
1
希望这个在最上面...几乎盲目地复制了很多代码! - akatakritos
2
很棒的答案 :-) 后面的SEO部分很适合放在HTTP模块中。 - David Kirkland
1
@Aaron Sherman Application_BeginRequest 部分应该放在哪里?当它在 public class RouteConfig 内部时,它会给我报错,当它在 if 外部时也是如此。 - Richard Mišenčík
1
@richard-mišenčík 将其添加到 Global.asax 文件中。 - ITmeze
显示剩余6条评论

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为http://goneale.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc/。 - Daniel Liuzzi
4
@德里克-不对,当使用区域时,教程会出现问题。尝试了3天所有的方法之后……我找到了一个更好的解决方案,有一个叫做Attribute Routing的库。它解决了这个问题,并使生活变得更加轻松。http://www.philliphaydon.com/2011/08/mvc-routing-with-attributes-makes-routing-awesome/ - Phill
6
对于MVC 4,有一种更好的解决方案是使用属性路由。LowercaseUrls = true; 更多信息请参见http://www.dhuvelle.com/2012/11/tips-for-aspnet-mvc-4-lowercase-urls.html。 - Marc Cals
4
@GONeale,你的网址怎么了?第二个链接已经失效了!尽管标题是“互联网上最好的Gone Ale网站”,但这让我笑了。 - Luke
2
@chteuchteu,那个链接对我不起作用,但是这个链接可以。 - chue x
显示剩余10条评论

33
如果您正在使用ASP.NET Core,那么您可能需要查看这个链接。

Add the following line to the ConfigureServices method of the Startup class.

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

1
同样适用于Core 2.0。此外,也可以在https://dev59.com/fFkS5IYBdhLWcg3wJDQP#45777372找到。 - yzorg

21
如果您正在使用UrlHelper生成链接,您可以简单地指定操作和控制器的名称为小写:
itemDelete.NavigateUrl = Url.Action("delete", "photos", new { key = item.Key });

结果为:/media/photos/delete/64(即使我的控制器和操作是帕斯卡命名法)。


17
我认为在一个中央位置完成这项工作是最简单和标准的解决方案。这就像内联 CSS 一样糟糕。(显然有15个人使用内联 CSS)。 - The Muffin Man
Linux服务器也是这样吗? - QMaster

16

我在Nick Berardi的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;
    }
}

然后修改 Global.asax.cs 文件中的 RegisterRoutes 方法。

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

但我希望知道如何使用routes.MapRoute的方法...


GONeale的文章提供了一个扩展方法,让你可以编写routes.MapRouteLowercase(...,这比上面那个更好:http://goneale.wordpress.com/2008/12/19/lowercase-route-urls-in-aspnet-mvc/ - Drew Noakes
1
GONeale的整个博客消失了。这里是另一篇博客文章,内容类似(并且使用了相同的扩展方法)。它在减少重复内容的背景下解决了这种情况。 - patridge

11
你可以通过将这个类作为一个扩展添加到RouteCollection中,继续使用MapRoute语法:
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

5

实际上这有两个答案:

  1. 您已经可以做到这一点:路由引擎进行大小写不敏感的比较。如果您输入小写路由,它将被路由到适当的控制器和操作。
  2. 如果您使用生成路由链接的控件(ActionLink、RouteLink等),它们将生成混合大小写的链接,除非您覆盖此默认行为。

至于下划线,您需要自己处理...


3

您能使用ActionName属性吗?

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

 }

我认为大小写不重要。URL中的More_Details、more_DETAILS、mOrE_DeTaILs都会带您到同一个控制器操作。


我还没有尝试过,它会让你使用其中任何一个吗?("moredetails"或"more_details") - GalacticCowboy
1
跟进一下,我试过了,它要求你使用指定的名称,所以无论如何,它都不允许你处理它。此外,根据您构建控制器操作和视图的方式,您可能需要明确指定视图的名称。 - GalacticCowboy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接