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

Posted

技术标签:

【中文标题】ASP.net MVC 对带有连字符的 URL 的支持【英文标题】:ASP.net MVC support for URL's with hyphens 【发布时间】:2011-01-05 11:12:27 【问题描述】:

是否有一种简单的方法可以让 MvcRouteHandler 将传入 URL 的操作和控制器部分中的所有连字符转换为下划线,因为连字符在方法或类名称中不受支持。

这样我就可以在继续使用 MapRoute 方法的同时支持诸如 sample.com/test-page/edit-details 映射到 Action edit_details 和 Controller test_pagecontroller 之类的结构。

我知道我可以指定一个动作名称属性并支持控制器名称中的连字符,这些连字符可以手动添加路由来实现这一点,但是我正在寻找一种自动化方式,以便在添加新控制器和动作时避免错误。

【问题讨论】:

连字符在 URL 中被视为对用户更友好。下划线与带下划线的链接冲突。 你可以看看这篇文章,展示如何实现custom MvcRouteHandler。 Asp.Net MVC: How do I enable dashes in my urls?的可能重复 任何来自 Google 搜索的人都可以查看 @dsteuernol 的答案,因为它建立在之前的答案之上,而 IMO 是我见过的所有解决方案中最强大的。 对于 ASP.NET Core -> ***.com/questions/53022924/… 【参考方案1】:

C# 版本的 John's Post 适合任何喜欢它的人:C# and VB version on my blog

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。 你会如何使用@html.Actionlink 来指向连字符的url? @Html.ActionLink("Test", "Action", "ControllerName-Hyphen") 在上述情况下,actionlink 不知道如何处理连字符,我是否必须创建另一个扩展名,将“-”替换为“” ?我希望生成的网址中包含连字符【参考方案2】:

我已经找到了解决方案。 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 来指向连字符的url? @Html.ActionLink("Test", "Action", "ControllerName-Hyphen") 在上述情况下,actionlink 不知道如何处理连字符,我是否必须创建另一个扩展名,将“-”替换为“” ?我希望生成的网址中包含连字符 用连字符写你的动作链接,当 MVC 查找路由时,它将使用 HyphenatedRouteHandler 来解析它并生成完整的 URL。 好吧,看起来它运行良好,我只是从 resharper 得到一个语法检查问题,因为它不知道映射谢谢【参考方案3】:

在这种情况下,您真正​​需要做的就是使用连字符为视图命名,因为您希望它出现在 URL 中,删除控制器中的连字符,然后添加一个包含连字符的 ActionName 属性。根本不需要下划线。

有一个名为 edit-details.aspx

的视图

并且有一个这样的控制器:

[ActionName("edit-details")]
public ActionResult EditDetails(int id)

    // your code

【讨论】:

这适用于动作,但不适用于控制器,至少在 MVC 1 中无效【参考方案4】:

我意识到这是一个相当老的问题,但对我来说,这只是接受带有连字符的 url 的故事的一半,另一半是生成这些 url,同时仍然能够使用 Html.ActionLink 和其他帮助程序MVC 框架,我通过创建一个类似的自定义路由类解决了这个问题,这里是代码,以防它帮助任何从谷歌搜索来到这里的人。它还包括 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())
    );

【讨论】:

这正是我想要的。 以上答案只是门票。十分感谢。我想提请注意 Sylvia J 的重要补充(目前如下)【参考方案5】:

感谢 dsteuernol 的回答——正是我想要的。但是我发现我需要增强 HyphenatedRouteHandler 以涵盖当前页面隐含 Controller 或 Area 的场景。例如使用@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;
        
    

将大写字母放回意味着隐含的控制器或区域随后被正确连字符。

【讨论】:

【参考方案6】:

我为此问题开发了一个开源 NuGet 库,它隐式地将 EveryMvc/Url 转换为 every-mvc/url。

虚线网址对 SEO 更友好且更易于阅读。 (More on my blog post)

NuGet 包:https://www.nuget.org/packages/LowercaseDashedRoute/

要安装它,只需在 Visual Studio 中打开 NuGet 窗口,方法是右键单击项目并选择 NuGet 包管理器,然后在“在线”选项卡上键入“小写虚线”,它应该会弹出。

或者,您可以在包管理器控制台中运行此代码:

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

【讨论】:

【参考方案7】:

不为每个url写地图不知道有什么办法:

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

【讨论】:

这就是我所说的“手动添加路由”的意思,也是我想要避免的。【参考方案8】:

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

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

与公认的解决方案相比,我更喜欢这种方法,它在控制器操作名称和视图文件名中留下下划线,在视图的 Url.Action 帮助程序中使用连字符。我更喜欢一致性,而不必记住名称是如何转换的。

【讨论】:

【参考方案9】:

在 MVC 5.2.7 中,您可以简单地使用属性指定

动作名称

[ActionName("Import-Export")]
public ActionResult ImportExport()

    return View();

然后给视图命名

导入-导出.cshtml

链接将是:

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

形式如下:

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

【讨论】:

以上是关于ASP.net MVC 对带有连字符的 URL 的支持的主要内容,如果未能解决你的问题,请参考以下文章

SEO:带有和不带有破折号“/”和 ASP.NET MVC 的重复 URL

无法显示带有 asp.net mvc url.action 链接的引导对话框

Asp.net core MVC post 参数始终为空

带有 ASP.NET 路由的原始、未处理的 URL

ASP.NET MVC 路由 - 将入站路由值自动传递给出站 URL?

更改 ASP.NET MVC 或 IIS 中的 URL 根路径