ASP.NET MVC 正则表达式路由约束

ASP.NET MVC regex route constraint

提问人:Fabian Vilers 提问时间:12/30/2010 最后编辑:CinCoutFabian Vilers 更新时间:10/5/2023 访问量:14316

问:

我在尝试在路由上设置的特定约束时遇到了问题。我的 URL 必须如下所示:http://hostname/id-my-title-can-be-that-long 其中 id 仅由数字组成,标题是带有破折号分隔符的小写字符。id 和标题也用破折号分隔。例如:http://hostname/123-my-title。

这是我的路线定义:

routes.MapRoute(
    "Test",
    "{id}-{title}",
    new { controller = "Article", action = "Index" },
    new { id = @"(\d)+", title = @"([a-z]+-?)+" }
);

使用 html 帮助程序正确生成 URL:

<%: Html.ActionLink("My link", "Index", "Article", new { id = Model.IdArticle, title = Model.UrlTitle }, null) %>

当然,其中 Model.IdArticle 是 Int32,Model.UrlTitle 是符合我要求的标题的预制字符串(仅小写,空格替换为破折号)。

问题是,当我点击链接时,没有调用正确的控制器和方法,它落在下一个错误的路由上。

作为记录,我在 MVC 2 ASP.NET。

有人有想法吗?

正则表达式 ASP.NET-MVC-2 约束 路由

评论

0赞 Fabian Vilers 12/30/2010
最后,我们将使用 undescore 将 ID 与标题分开。我仍然想知道为什么这不起作用,但它不再阻塞。感谢您的帮助!

答:

0赞 WestDiscGolf 12/30/2010 #1

您可以尝试传递整个路由字符串“{id}-{title}”,并在字符串进入您的操作之前手动解析该字符串,方法是执行类似于 Phil Haack 的 slug to id actionfilter 属性 - link

希望这会有所帮助。

评论

0赞 Fabian Vilers 12/30/2010
我之前读过那篇博文,但当我可以拆分路线信息时,我发现它有点矫枉过正。由于我的 URL 可以从路由创建,但不能解析为路由,因此我正在考虑 MVC 中的某种错误。
0赞 WestDiscGolf 12/30/2010
啊,很公平。会有另一个想法:-)
0赞 Chao 12/30/2010 #2

路由中的几个字符是“特殊”的,将拆分 - 和 / 等参数。可能是路由中的额外 -s 导致它失败。尝试这样做,使标题包含以下所有内容。"{id}-{*title}"

更新

上面的答案是当你在喝够咖啡之前继续使用 StackOverflow 时会发生什么。

我们在处理用户上传的文件的文件名时遇到了同样的问题,路由包含“-”作为分隔符,但也可以在后面参数的值中使用,它可以生成正确的 URL,但不匹配。最后,我编写了一个 SpecialFileRoute 类来处理这个问题并注册了此路由。虽然有点丑,但确实如此。

请注意,我保留了旧式的 MVC 路由来生成 URL,我正在尝试让它正确地完成它,但这是稍后再回来的东西。

    /// <summary>
/// Special route to handle hyphens in the filename, a catchall parameter in the commented route caused exceptions
/// </summary>
public class SpecialFileRoute : RouteBase, IRouteWithArea
{
    public string Controller { get; set; }
    public string Action { get; set; }
    public IRouteHandler RouteHandler = new MvcRouteHandler();
    public string Area { get; private set; }

    //Doc/{doccode} - {CatNumber}.{version} - {*filename},

    public SpecialFileRoute(string area)
    {
        Area = area;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        string url = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2);
        var urlmatch = Regex.Match(url, @"doc/(\w*) - (\d*).(\d*) - (.*)", RegexOptions.IgnoreCase);
        if (urlmatch.Success)
        {
            var routeData = new RouteData(this, this.RouteHandler);

            routeData.Values.Add("doccode", urlmatch.Groups[1].Value);
            routeData.Values.Add("CatNumber", urlmatch.Groups[2].Value);
            routeData.Values.Add("version", urlmatch.Groups[3].Value);
            routeData.Values.Add("filename", urlmatch.Groups[4].Value);
            routeData.Values.Add("controller", this.Controller);
            routeData.Values.Add("action", this.Action);
            return routeData;
        }
        else
            return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        if (values.ContainsKey("controller") && (!string.Equals(Controller, values["controller"] as string, StringComparison.InvariantCultureIgnoreCase)))
            return null;
        if (values.ContainsKey("action") && (!string.Equals(Action, values["action"] as string, StringComparison.InvariantCultureIgnoreCase)))
            return null;
        if ((!values.ContainsKey("contentUrl")) || (!values.ContainsKey("format")))
            return null;
        return new VirtualPathData(this, string.Format("{0}.{1}", values["contentUrl"], values["format"]));
    }
}

路由添加如下:

context.Routes.Add(new SpecialFileRoute(AreaName) { Controller = "Doc", Action = "Download" });

如上所述,这有点丑陋,当我有时间时,我想做很多工作来改进这一点,但它解决了将 URL 拆分为所需参数的问题。它与这一条路由的特定要求密切相关,具有 url 模式、正则表达式和值硬编码,尽管它应该给你一个开始。

评论

0赞 Fabian Vilers 12/30/2010
感谢您提供的信息,但我们不能在包含多个部分的区段中使用 catchall (*) 参数。
0赞 Chao 12/30/2010
哦,当然。不久前我遇到了同样的问题
0赞 chaos 5/1/2022 #3

你不会有一个好时机,用一个字符来分隔ID与标题,这也是标题模式的一部分。如果可以的话,我建议只使用。{id}/{title}