ASP.net MVC对带有连字符的URL的支持

35

有没有一种简单的方法可以让MvcRouteHandler将传入URL的动作(Action)和控制器(Controller)部分中的所有连字符转换为下划线,因为在方法或类名中不支持使用连字符。

这样做是为了支持这样的结构:sample.com/test-page/edit-details 映射到 Action edit_details 和 Controller test_pagecontroller,同时继续使用MapRoute方法。

我知道我可以指定动作名称属性并支持带连字符的控制器名称,但是我正在寻找一种自动化的方式,以避免添加新控制器和动作时出现错误。


6
连字符在URL中被视为更加用户友好的选项。下划线与带下划线的链接不兼容。 - Will Morgan
你可以看一下这篇文章,它展示了如何实现一个自定义MvcRouteHandler - Darin Dimitrov
2
可能是重复的问题:Asp.Net MVC:如何在我的URL中启用破折号? - martin clayton
3
来自谷歌搜索的任何人都可以查看 @dsteuernol 的回答,因为它在之前的回答基础上进行了完善,并且在我看来是我见过的最强大的解决方案。 - The Muffin Man
1
针对 ASP.NET Core -> https://dev59.com/uq_la4cB1Zd3GeqPsWeG#63012154 - Hamed
9个回答

36

以下是约翰的文章的 C# 版本,供希望使用 C# 的所有人使用:我的博客上提供了 C# 和 VB 版本

public class HyphenatedRouteHandler : MvcRouteHandler{
        protected override IHttpHandler  GetHttpHandler(RequestContext requestContext)
        {
            requestContext.RouteData.Values["controller"] = requestContext.RouteData.Values["controller"].ToString().Replace("-", "_");
            requestContext.RouteData.Values["action"] = requestContext.RouteData.Values["action"].ToString().Replace("-", "_");
            return base.GetHttpHandler(requestContext);
        }
    }

...以及新的路由:

routes.Add(
            new Route("{controller}/{action}/{id}", 
                new RouteValueDictionary(
                    new { controller = "Default", action = "Index", id = "" }),
                    new HyphenatedRouteHandler())
        );
你也可以使用以下方法,但请记住你需要将视图命名为"My-Action",如果你喜欢让Visual Studio自动生成视图文件,则可能会很烦人。
[ActionName("My-Action")]
public ActionResult MyAction() {
    return View();
}

这个很好用...但是要注意 controller = "Default"。在注意到并将其更改为“Home”之前,我遇到了几次404错误。 - Jonathan Freeland
2
你如何使用@Html.Actionlink指向连字符url?@Html.ActionLink("测试", "Action", "ControllerName-Hyphen")在上述情况下,actionlink不知道如何处理连字符,我是否需要创建另一个扩展来替换“-”为“”?我希望生成的url中有连字符。 - DotnetShadow

18

我已经想出了一个解决方案。MvcRouteHandler中的requestContext包含控制器和操作的值,您可以对其进行简单的替换。

Public Class HyphenatedRouteHandler
    Inherits MvcRouteHandler

    Protected Overrides Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) As System.Web.IHttpHandler
        requestContext.RouteData.Values("controller") = requestContext.RouteData.Values("controller").ToString.Replace("-", "_")
        requestContext.RouteData.Values("action") = requestContext.RouteData.Values("action").ToString.Replace("-", "_")
        Return MyBase.GetHttpHandler(requestContext)
    End Function

End Class

那么你所需要做的就是将 routes.MapRoute 替换为等效的 routes.Add,并指定新的路由处理程序。这是必须的,因为 MapRoute 不允许您指定自定义的路由处理程序。

routes.Add(New Route("{controller}/{action}/{id}", New RouteValueDictionary(New With {.controller = "Home", .action = "Index", .id = ""}), New HyphenatedRouteHandler()))

你如何使用@Html.Actionlink指向连字符网址?@Html.ActionLink("测试", "操作", "控制器名称-连字符")在上述情况下,actionlink不知道如何处理连字符,我是否需要创建另一个扩展来将“ - ”替换为“”?我希望生成的网址中有连字符。 - DotnetShadow
将您的操作链接使用连字符写入,当MVC查找路由时,它将使用HyphenatedRouteHandler来解析并生成完整的URL。 - John
好的,看起来它确实工作得很好,我只是因为Resharper不知道这个映射而遇到了语法检查问题。谢谢。 - DotnetShadow

14

在这种情况下,您需要以连字符的形式命名视图,以便在URL中显示。将控制器中的连字符删除,然后添加一个ActionName属性,其中包含连字符即可。完全不需要使用下划线。

例如,将视图命名为edit-details.aspx

并创建以下控制器:

[ActionName("edit-details")]
public ActionResult EditDetails(int id)
{
    // your code
}

这对于动作来说是有效的,但对于控制器来说却不行,至少在MVC 1中不行。 - John

9

我知道这个问题已经很老了,但对于我来说,接受具有连字符的URL只是其中一半的事情,另一半则是在仍然能够使用MVC框架中的Html.ActionLink和其他助手的情况下生成这些URL。我通过创建一个类似的自定义路由类来解决这个问题,以下是代码,如果有人从谷歌搜索中来到这里,它可能会有所帮助。它还包括将URL转换为小写字母的功能。

public class SeoFriendlyRoute : Route
{
     // constructor overrides from Route go here, there is 4 of them

     public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
     {
          var path = base.GetVirtualPath(requestContext, values);

          if (path != null)
          {
              var indexes = new List<int>();
              var charArray = path.VirtualPath.Split('?')[0].ToCharArray();
              for (int index = 0; index < charArray.Length; index++)
              {
                  var c = charArray[index];
                  if (index > 0 && char.IsUpper(c) && charArray[index - 1] != '/')
                      indexes.Add(index);
              }

              indexes.Reverse();
              indexes.Remove(0);
              foreach (var index in indexes)
                  path.VirtualPath = path.VirtualPath.Insert(index, "-");

              path.VirtualPath = path.VirtualPath.ToLowerInvariant();
          }

          return path;
     }
}

在添加路由时,您可以创建一个RouteCollection扩展或者只需在全局路由声明中使用以下内容。

routes.Add(
        new SeoFriendlyRoute("{controller}/{action}/{id}", 
            new RouteValueDictionary(
                new { controller = "Default", action = "Index", id = "" }),
                new HyphenatedRouteHandler())
    );

1
以上答案正是我所需要的。非常感谢。我想要引起大家对Sylvia J(目前在下面)重要补充的注意。 - Martin Hansen Lennox

2

感谢dsteuernol提供的答案 - 正是我所需要的。但是我发现我需要改进HyphenatedRouteHandler以涵盖从当前页面暗示控制器或区域的情况。例如,使用@Html.ActionLink("My Link", "Index")

我将GetHttpHandler方法更改如下:

public class HyphenatedRouteHandler : MvcRouteHandler
    {
        /// <summary>
        /// Returns the HTTP handler by using the specified HTTP context.
        /// </summary>
        /// <param name="requestContext">The request context.</param>
        /// <returns>
        /// The HTTP handler.
        /// </returns>
        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {

            requestContext.RouteData.Values["controller"] = ReFormatString(requestContext.RouteData.Values["controller"].ToString());
            requestContext.RouteData.Values["action"] = ReFormatString(requestContext.RouteData.Values["action"].ToString());

            // is there an area
            if (requestContext.RouteData.DataTokens.ContainsKey("area"))
            {
                requestContext.RouteData.DataTokens["area"] = ReFormatString(requestContext.RouteData.DataTokens["area"].ToString());
            }

            return base.GetHttpHandler(requestContext);
        }


        private string ReFormatString(string hyphenedString)
        {
            // lets put capitals back in

            // change dashes to spaces
            hyphenedString = hyphenedString.Replace("-", " ");

            // change to title case
            hyphenedString = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(hyphenedString);

            // remove spaces
            hyphenedString = hyphenedString.Replace(" ", "");

            return hyphenedString;
        }
    }

将首字母大写的方式重新应用到文本中意味着隐含的控制器或区域将得到正确的连字符处理。

1
我已经为这个问题开发了一个开源的NuGet库,它将EveryMvc/Url隐式转换为every-mvc/url。
虚线链接更加友好且易于阅读。(在我的博客文章中了解更多
NuGet包:https://www.nuget.org/packages/LowercaseDashedRoute/ 要安装它,只需通过右键单击项目并选择NuGet包管理器,在“在线”选项卡中输入“Lowercase Dashed Route”,它就会弹出。
或者,您可以在程序包管理器控制台中运行此代码:Install-Package LowercaseDashedRoute 之后,您应该打开App_Start/RouteConfig.cs文件,并注释掉现有的route.MapRoute(...)调用,然后添加以下内容:
routes.Add(new LowercaseDashedRoute("{controller}/{action}/{id}",
  new RouteValueDictionary(
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }),
    new DashedRouteHandler()
  )
);

就这样。所有的url都是小写、有破折号,并且会自动转换,无需其他操作。

开源项目链接: https://github.com/AtaS/lowercase-dashed-route


0

如果您将项目升级到MVC5,您可以使用属性路由。

[Route("controller/my-action")]
public ActionResult MyAction() {
    return View();
}

我更喜欢这种方法,而不是接受的解决方案,因为后者会在控制器操作名称和视图文件名中留下下划线,在视图的Url.Action助手中留下连字符。我更喜欢一致性,而不必记住名称如何转换。

0

不知道有没有不用为每个URL编写映射的方法:

routes.MapRoute("EditDetails", "test-page/edit-details/{id}", new { controller = "test_page", action = "edit_details" });

这就是我所说的“手动添加路由”的意思,也是我想要避免的。 - John

0
在MVC 5.2.7中,您可以简单地使用属性指定ActionName。
[ActionName("Import-Export")]
public ActionResult ImportExport()
{
    return View();
}

然后给视图命名

Import-Export.cshtml

链接将会是:

@Html.ActionLink("Import and Export", "Import-Export", "Services")

这是一个形式为:

@Html.ActionLink("LinkName", "ActionName", "ControllerName")

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