33

I have several routes defined in my Global.asax;

When I'm on a page I need to figure out what is the route name of the current route, because route name drives my site menu.

How can this be done?

p.campbell
  • 98,673
  • 67
  • 256
  • 322
Andrey
  • 20,487
  • 26
  • 108
  • 176

10 Answers10

24

Unfortunately, it's not possible to get the route name of the route because the name is not a property of the Route. When adding routes to the RouteTable, the name is used as an internal index for the route and it's never exposed.

There's one way to do this.

When you register a route, set a DataToken on the route with the route name and use that to filter routes.

The easiest way to do #1 is to probably write your own extension methods for mapping routes.

Haacked
  • 58,045
  • 14
  • 90
  • 114
11

FWIW, since the extensions and example shown by @Simon_Weaver are MVC-based and the post is tagged with WebForms, I thought I'd share my WebForms-based extension methods:

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

So now in Global.asax.cs when you're registering your routes, instead of doing like routes.MapPageRoute(...) - instead use the extension method and do routes.MapPageRouteWithName(...)

Then when you want to check what route you're on, simply do Page.RouteData.GetRouteName()

That's it. No reflection, and the only hard-coded references to "route-name" are in the two extension methods (which could be replaced with a const if you really wanted to).

kman
  • 2,184
  • 3
  • 23
  • 42
11

If you're working with a small subset of important routes you need to check for (a special case or two) you can just do this :

if (routeData.Route == RouteTable.Routes["gallery-route"])
{
   // current route is 'gallery-route'
}

A common reason for needing the route name is for debugging purposes. A quick and dirty way to do this follows - but you'll need to add each route name to the array of names. Should be fine for debugging - especially if the code isn't running during production.

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

For anything beyond this you should take the (minimal) time needed for @haacked's solution.

DeveloperDan
  • 4,626
  • 9
  • 40
  • 65
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • Thanks for the hint. Your first idea worked brilliantly for me, cuz I just needed to check in an article page whether they came through the title route or rowid route. CheerZ! – eidylon Dec 23 '10 at 20:01
7

Here's an implementation of @haacked's suggestion - with also a simple 'razor' table to display route data.

Note: You may not have realized that all the standard 'MapRoute' methods are actually extension methods. Therefore we cannot use the same name. I've just called it 'MapRoute2', because right now thats all I can think of.

You must replace all calls to MapRoute with a call to MapRoute2, don't forget all AreaRegistration files as well as global.asax.cs

Extension method:

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

Here's a simple razor .cshtml file I'm using to display routing information:

<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>
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • That's pretty much how I've implemented it back in October when Haacked suggested it :) Anyway thanks for the tip – Andrey Dec 19 '10 at 16:45
  • i guess thats the only way ;-) its just a bit of a pain writing all those overloads - and then you realize you need double for areas – Simon_Weaver Dec 19 '10 at 20:20
6

I would up-vote Simon_Weaver's answer but unfortunately I just joined and do not have the reputation points to do so.

Adding to his answer, because it was exactly what I was looking for, here's the way I do it:

I have a public enum "PageRouteTable":

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

I use this enum when building the routes:

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

I then created a Page extension method:

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

Now in my pages I can simply use a switch to act upon it:

PageRouteTable? currentRoute = this.CurrentRoute();
if (currentRoute.HasValue) {
    switch(currentRoute.Value) {
        case PageRouteTable.UserArea_Default:
            // Act accordingly
            break;
        .
        .
        .
    }
}

I also have the benefit of explicitly defined variables and do not have to worry about coding against strings. This saves me a ton of headaches in maintenance.

-- happy coding.

MikeAtCodeSmart
  • 591
  • 7
  • 14
3

For C# you can declare your routes like so:

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

Then in your method where you need the route you can then call the following:

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

And you will have a string set in your global.asax to then use as you need.

Andrew Berry
  • 853
  • 3
  • 10
  • 25
3

RouteCollection maintains a private dictionary of named routes.

Route names can be coaxed out of it by

  1. using reflection to retrieve the value of the private field and
  2. querying the dictionary for the item whose value is the route.

The extension method below follows this process:

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;
}
Samu Lang
  • 2,261
  • 2
  • 16
  • 32
1

You can add every route parameters and its not necessary this parameters be in your Url: You can put your route name as a parameter like this inGlobal.asax:

 routes.MapPageRoute("Page",
                "Page-{ID}",
               "~/Item_show.aspx", false, new RouteValueDictionary{ { "RouteName" , "Page" }});

And Access it in your page:

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

               }  

           }

Best way is not hard way.

Farhawd
  • 35
  • 1
  • 5
1

I have been facing the same dilemma, and I came to the conclusion that unfortunately, there doesn't seem to be a way to find out which route (by its name) the ASP.NET has picked for usage.

It seems you can only figure that out by the names of the parameters that might exist in your route - those will show up in the RouteData.Values dictionary.

If someone knows a way to somehow get at the actual name of the route picked by ASP.NET for a given URL, I'd be interested to know how to do that myself, too!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • Yeah, that stinks! They have a private map that holds names and routes, but there is no way of exposing it :( – Andrey Oct 18 '10 at 00:01
  • Actually, Haacked's solution works very well, it's in my production site now :) – Andrey Dec 19 '10 at 16:46
  • @Andrey: yeah, but it's a haack :-) and I'd really prefer a "proper" version without having to set DataTokens myself - just to get the name of the route back... – marc_s Dec 19 '10 at 18:27
0

A simple approach that I've implemented is to supply a named key for the 'defaults' param of the .MapPageRoute method. Use a constant for the default, and you can pull it out of the Page.RouteData.Values collection the same way you would normally.

example (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") gives me 'stats'

ScottE
  • 21,530
  • 18
  • 94
  • 131