So, I’ve been trying for a few hours now to workaround something that should theoretically be very simple. Let’s take this sample url:
http://sample.com/products/in/texas/dallas
This maps onto a specific route:
routes.MapRoute(
"products",
"products/in/{state}/{city}",
new { controller = "Products", action = "List", state = UrlParameter.Optional, city = UrlParameter.Optional });
In my action method, I can do lookups to make sure “texas” and “dallas” exist, and that “dallas” exists within Texas. That’s all fine and dandy. However, in the situation where the city doesnt exist (either because the geo is incorrect, or mispelled), I want it to back up the state level. Example:
http://sample.com/products/in/texas/dallax
That should issue a redirect to
http://sample.com/products/in/texas
The “easy” way to do this was to simply issue a Redirect call like so:
return Redirect("/products/in/" + stateName);
However, I’m trying to decouple this from the URL structure; for example, if we ever decided to change how the route looks (say, change the pattern to products/around/{state}/{city}), then I would have to know that I need to make updates to this controller to fix the URL redirect.
If I can make a solution that just inspects the route values and can figure things out, then I don’t have to worry if I change the route pattern, because the route values could still be figured out.
Ideally, I would have liked to do something like this:
return RedirectToRoute(new { controller = "Products", action = "List", state = state });
(Note, that is is a simplified example; the “required” route pieces like controller name and action method name would be determined by Generic argument and Expression inspection respectively).
That actually performs the redirect, HOWEVER, the route values from the current request get appended onto the redirect and thus you get in a redirect loop (note that I didn’t include the city route value in the route object). How do I stop the “city” route value from being included in this redirect?
I’ve tried the following things to get rid of the route value:
- Compose my own
RouteValueDictionary/ anonymous route data object and pass that to the overload ofRedirectToRoute. - Create my own action result to inspect
RouteTable.Routesand find the route myself, and do the replacement of the tokens myself. This seems the most “kludgy”, and would seem to be re-inventing the wheel. - Make a method like
RedirectWithoutthat takes a key value and callsRedirectToRouteResult.RouteValues.Remove(key)– which also didnt work. - I’ve also attempted to add a null value for the key I don’t want to add; however, this alters the route to something that isnt correct – ie
new { controller = "Products", action = "List", state = stateName, city = (string)null }issues a redirect to/Products/List?state=Texaswhich is not the right URL.
It all seems to stem from the RedirectToRoute taking the current request context to construct the virtual path data. Is there a workaround?
If you were using T4MVC, you should be able to do something like this:
Have you tried this?
Reply to comments
Maybe MVC is confused because your optional parameter is not at the end. The below should work with the above
RedirectToRoutethat specifiescity = null:You can then add another route to handle URL’s where
stateis optional: