如何从RouteData中获取路由名称?

33
我在Global.asax中定义了几个路由;当我在页面上时,我需要找出当前路由的路由名称,因为路由名称驱动我的站点菜单。这该如何实现?
10个回答

24

很遗憾,因为名称不是Route的属性,所以无法获取路由名称。在将路由添加到RouteTable时,名称用作路由的内部索引,并且永远不会公开。

有一种方法可以解决这个问题。

在注册路由时,在路由上设置一个DataToken,并使用它来过滤路由。

做#1的最简单方法可能是编写自己的扩展方法以映射路由。


@Simon_Weaver 在MVC 4中,Name仍然不是一个属性。 - Matt Kocaj
2
MVC 5 中仍不是 Route 的属性! - Triynko

11
如果你只需要检查一小部分重要的路由(几个特殊情况),你可以这样做:
if (routeData.Route == RouteTable.Routes["gallery-route"])
{
   // current route is 'gallery-route'
}

需要路由名称的常见原因是为了调试目的。下面是一种快速而不太规范的方法 - 但是您需要将每个路由名称添加到名称数组中。这对于调试应该没问题 - 尤其是如果代码在生产期间没有运行。

// quick and dirty way to get route name
public string GetRouteName(RouteData routeData) 
{
    foreach (string name in new [] { "gallery-route", 
                                     "products-route", 
                                     "affiliate-route", 
                                     "default" }) 
    {
        if (routeData.Route == RouteTable.Routes[name])
        {
            return name;
        }
    }
    return "UNKNOWN-ROUTE";   // or throw exception
}

如果你需要更多的功能,你应该花费(最少的)时间去使用 @haacked 的解决方案。


谢谢你的提示。你的第一个想法对我非常有用,因为我只需要在文章页面中检查它们是通过标题路由还是行ID路由进入的。加油! - eidylon

11

值得一提的是,由于@Simon_Weaver所展示的扩展和示例都是基于MVC的,而这篇文章被标记为WebForms,所以我想分享我基于WebForms的扩展方法:

    public static void MapPageRouteWithName(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess = true,
            RouteValueDictionary defaults = default(RouteValueDictionary), RouteValueDictionary constraints = default(RouteValueDictionary), RouteValueDictionary dataTokens = default(RouteValueDictionary))
    {
        if (dataTokens == null)
            dataTokens = new RouteValueDictionary();

        dataTokens.Add("route-name", routeName);
        routes.MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, defaults, constraints, dataTokens);
    }

    public static string GetRouteName(this RouteData routeData) 
    {
        if (routeData.DataTokens["route-name"] != null)
            return routeData.DataTokens["route-name"].ToString();
        else return String.Empty;
    }

现在,在Global.asax.cs中注册路由时,不要像routes.MapPageRoute(...)那样操作 - 而是使用扩展方法,即routes.MapPageRouteWithName(...)。

然后,当您想要检查所在的路由时,只需执行Page.RouteData.GetRouteName()。

这就是全部内容。没有反射,并且对“route-name”的唯一硬编码引用在两个扩展方法中(如果您真的想这样做,可以将其替换为const)。


3
+1,感谢您为我节省了MVC到WebForms的翻译练习。 - Evan Haas

7
这是@haacked建议的一种实现方式——还包括一个简单的“剃刀”表来显示路由数据。
注意:您可能没有意识到所有标准的“MapRoute”方法实际上都是扩展方法。因此,我们不能使用相同的名称。我只是称其为“MapRoute2”,因为眼下这是我所能想到的。 您必须将所有对MapRoute的调用替换为对MapRoute2的调用,不要忘记修改所有AreaRegistration文件以及global.asax.cs文件 扩展方法:
public static class RouteNameExtensions
{
    // RouteCollection
    public static Route MapRoute2(this RouteCollection routes, string name, string url)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url));
    }

    public static Route MapRoute2(this RouteCollection routes, string name, string url, object defaults)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults));
    }

    public static Route MapRoute2(this RouteCollection routes, string name, string url, string[] namespaces)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, namespaces));
    }

    public static Route MapRoute2(this RouteCollection routes, string name, string url, object defaults, object constraints)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults, constraints));
    }

    public static Route MapRoute2(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults, namespaces));
    }

    public static Route MapRoute2(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults, constraints, namespaces));
    }

    // AreaRegistrationContext
    public static Route MapRoute2(this AreaRegistrationContext routes, string name, string url)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url));
    }

    public static Route MapRoute2(this AreaRegistrationContext routes, string name, string url, object defaults)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults));
    }

    public static Route MapRoute2(this AreaRegistrationContext routes, string name, string url, string[] namespaces)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, namespaces));
    }

    public static Route MapRoute2(this AreaRegistrationContext routes, string name, string url, object defaults, object constraints)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults, constraints));
    }

    public static Route MapRoute2(this AreaRegistrationContext routes, string name, string url, object defaults, string[] namespaces)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults, namespaces));
    }

    public static Route MapRoute2(this AreaRegistrationContext routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
        return AddRouteNameDataToken(name, routes.MapRoute(name, url, defaults, constraints, namespaces));
    }

    private static Route AddRouteNameDataToken(string name, Route route)
    {
        route.DataTokens["route-name"] = name;
        return route;
    }
}

以下是我使用的简单 Razor .cshtml 文件,用于显示路由信息:

<table class="basic-table">
    <tr>
        <th>Route Key</th>
        <th>Value</th>
    </tr>

    <tr>
        <td><strong>Route name</strong></td>
        <td>@ViewContext.RouteData.DataTokens["route-name"]</td>
    </tr>

    @foreach (var route in ViewContext.RouteData.Values)
    {
        <tr>
            <td>- @route.Key</td>
            <td>@(route.Value ?? "<null>")</td>
        </tr>
    }
    @foreach (var route in ViewContext.RouteData.DataTokens.Where(x=>x.Key != "route-name"))
    {
        <tr>
            <td><strong>@route.Key</strong></td>
            <td>@(route.Value ?? "<null>")</td>
        </tr>
    }

</table>

这基本上就是我在去年十月份实现它时Haacked建议的方式 :) 无论如何,感谢您的提示。 - Andrey
我猜那是唯一的方法;-)写所有这些过载有点麻烦,然后你意识到你需要双倍的面积。 - Simon_Weaver

6

我很想为Simon_Weaver的回答点赞,但很遗憾我刚加入并没有足够的声望点数。

补充一下他的回答,因为那正是我在找的,这是我做的方式:

我有一个公共枚举“PageRouteTable”:

public enum PageRouteTable
{
    // -- User Area
    UserArea_Locations,
    UserArea_Default,
    UserArea_PasswordReset,
    UserArea_Settings,
    .
    .
    .
}

我在构建路由时使用此枚举:

/* -- User Area Routes -- */
routes.MapPageRoute(PageRouteTable.UserArea_Default.ToString(), "home", "~/UserArea/Default.aspx");

我接下来创建了一个页面扩展方法:
public static PageRouteTable? CurrentRoute(this Page p)
{
    string[] pageRoutes = Enum.GetNames(typeof (PageRouteTable));
    foreach (string pageRoute in pageRoutes)
    {
        if (p.RouteData.Route == RouteTable.Routes[pageRoute])
        {
            return (PageRouteTable)Enum.Parse(typeof (PageRouteTable), pageRoute);
        }
    }
    return null;
}

现在,在我的页面中,我可以简单地使用开关来执行它:
PageRouteTable? currentRoute = this.CurrentRoute();
if (currentRoute.HasValue) {
    switch(currentRoute.Value) {
        case PageRouteTable.UserArea_Default:
            // Act accordingly
            break;
        .
        .
        .
    }
}

我也享受显式定义变量的好处,不必担心针对字符串编码。这在维护时可以省去很多麻烦。
-- 编码愉快。

3
对于C#,您可以这样声明路由:
        routeCollection.MapPageRoute("RouteForProduct", "Product/{ProductName}", "~/IRShop.aspx", false, new RouteValueDictionary { { "Section", "product" } });
        routeCollection.MapPageRoute("RouteForProductList", "ProductList/{CatName}", "~/IRShop.aspx", false, new RouteValueDictionary { { "Section", "productlist" } });
        routeCollection.MapPageRoute("RouteForContentList", "Content/{PageName}", "~/IRShop.aspx", false, new RouteValueDictionary { { "Section", "content" } });

在需要路由的方法中,您可以调用以下内容:

var x = Page.RouteData.Values["Section"].ToString();

你可以在 global.asax 文件中设置一个字符串,然后按照需要使用它。


3

RouteCollection维护一个私有的命名路由字典。

可以通过以下方法获取路由的名称:

  1. 使用反射来检索私有字段的值;
  2. 查询字典以获取其值为路由的项。

下面的扩展方法遵循这个过程:

public static string Name(this RouteBase original)
{
    var routes = System.Web.Routing.RouteTable.Routes;

    if (routes.Contains(original))
    {
        var namedMapField = routes.GetType().GetField("_namedMap", BindingFlags.NonPublic | BindingFlags.Instance);
        var namedMap = namedMapField.GetValue(routes) as Dictionary<string, RouteBase>;

        var query = 
            from pair in namedMap 
            where pair.Value == original 
            select pair.Key;

        return query.Single();
    }

    return string.Empty;
}

1
您可以添加每个路由参数,而这些参数不一定需要出现在URL中。您可以将路由名称作为参数放入Global.asax中,如下所示:
 routes.MapPageRoute("Page",
                "Page-{ID}",
               "~/Item_show.aspx", false, new RouteValueDictionary{ { "RouteName" , "Page" }});

在你的页面中访问它:

if (RouteData.Values["RouteName"] != null)
           {
               if (RouteData.Values["RouteName"].ToString() == "Page")
               {
                   Response.Write(RouteData.Values["RouteName"]);

               }  

           }

最好的方法不是艰难的方法。


1

我一直面临着同样的困境,我得出的结论是,不幸的是,似乎没有办法找出ASP.NET选择使用哪个路由(按其名称)。

看起来你只能通过你的路由中可能存在的参数名称来弄清楚这一点 - 这些将显示在RouteData.Values字典中。

如果有人知道如何以某种方式获取ASP.NET为给定URL选择的实际路由名称,我也很想知道如何做到这一点!


是啊,那真糟糕!他们有一个私有地图,保存着名称和路线,但没有任何方法可以公开它 :( - Andrey
实际上,Haacked的解决方案非常好用,现在已经在我的生产网站上使用了 :) - Andrey
@Andrey:是的,但这只是一个hack :-) 我真的更喜欢一个“正式”的版本,而不必自己设置DataTokens - 只是为了获取路由的名称... - marc_s

0
我实现的一个简单方法是为.MapPageRoute方法的“defaults”参数提供一个命名键。使用常量作为默认值,您可以像通常一样从Page.RouteData.Values集合中提取它。
示例(vb.net)
routes.MapPageRoute("league-division-stats", "{league}/{division}/stats", "~/routes/league-division-stats.aspx", False, New RouteValueDictionary(New With {.section = "stats"}))

Page.RouteData.Values("section") 给了我 'stats'


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