views:

35

answers:

2

I have a pair of routes that both point to the same action and depending on the supplied route data I would like one route to be chosen over the other. Trouble is, the routing engine seems to be automatically appending route values that I am explicitly telling it not to.

routes.MapRoute(
 "Products_Search_Paged",
 "products/search/page{pageNbr}",
 new { controller = "Products", action = "Search" },
 new { pageNbr = @"\d+" },
 new[] { "SantekCMS.Web.Components.Controllers" }
);

routes.MapRoute(
 "Products_Search",
 "products/search",
 new { controller = "Products", action = "Search" },
 new[] { "SantekCMS.Web.Components.Controllers" }
);

Note, the only difference between the two routes is the addition of /page{pageNbr} in Products_Search_Paged.

The problem I am experiencing is that when I am viewing the route Products_Search_Paged at /products/search/page2?name=foo, for example, and I want to create a link to /products/search, if I call Url.Action("Search, "Products", new { pageNbr = "" }) it is ignoring my declaration pageNbr = "" and creating the link /products/search?pageNbr=2. Alternatively, if I create a link with Url.Action("Search", "Products", new { pageNbr = 5 }) I end up with /products/search/page5, as expected.

I've tried reorganizing the routes and placing Products_Search before Products_Search_Paged which seems to eliminate the problem with ?pageNbr=# being appended despite nulling it, but that had the added effect of it not formatting the Products_Search_Paged as /page3 and rather producing /products/search?pageNbr=3.

From the testing I've performed thus far, I cannot find any reason why the pageNbr route value is getting appended as its current value even when I explicitly set it to something else in a provided RouteValueDictionary.

The two solutions to this problem that come to mind currently are either to use Url.RouteLink() to explicitly define which route I want to use, or to manually remove the pageNbr key from the RouteData.DataTokens prior to generating the URL. Before I choose one of those solutions though, I was hoping somebody might be able to identify why my current, seemingly legitimate approach, is not producing the results expected.

A: 

The problem appears to be your regex constraining the value of pageNbr- empty string does not match \d+. You have explicitly put it into the routing dictionary, so the route that uses it is the best match...

I haven't tested alternatives (currently away from my windows box), but giving it a default value of UrlParameter.Optional may help. You could also try changing the regex to \d*.

Jon
This was actually brought to my attention by someone else and I've tried both of these approaches but it doesn't really solve the problem because {pageNbr} is not an optional parameter. If the user tries to visit */products/search/page* it shouldn't work at all.
Nathan Taylor
+1  A: 

Flip the declarations, add a default value of pageNbr to the pageless URL, and then invoke it with a pageNbr of 1. That also has the advantage that links wit pagenbr of 1 will be the simpler link.

Brad Wilson
This seems to work, but if I'm understanding you correctly I must always call pageNbr=1 to get the pageless route?
Nathan Taylor
Yes, you'll want to pass pageNbr=1 all the time, in case you're on a URL that already has pageNbr in it.
Brad Wilson
Okay, sounds reasonable. Thanks Brad
Nathan Taylor