The question is similar to asp.net mvc Html.ActionLink() keeping route value I don't want, but with a twist that makes it more complex.
Starting from a default new MVC3 app, I change the routes to:
routes.MapRoute(
"r1", // Route name
"{controller}/{id}/{action}"
);
routes.MapRoute(
"r2", // Route name
"{controller}/{action}"
);
Notice that the id comes before the action in the first.
Then in Home\Index.cshtml, I add:
@Url.Action("Index")
@Url.Action("Index", new { id = "blah" })
@Url.Action("Index", new { id = "" })
Now I navigate to /Home/Foo/Index and look at the 3 generated links. I get
The first two make sense, and are using the first route.
But in the third link, which hits the second route, I don't understand why id=Foo is passed on the query string, given that I explicitly passed an empty id. I would expect it to just generate "/Home/Index".
Can anyone explain that, and suggest how I can get it not to show up?
You use route constraints to restrict the browser requests that match a particular route. You can use a regular expression to specify a route constraint. For example, imagine that you have defined the route in Listing 1 in your Global. asax file.
Route values are the values extracted from a URL based on a given route template. Each route parameter in a template will have an associated route value and is stored as a string pair in a dictionary.
As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API. For example, you can easily create URIs that describe hierarchies of resources. The earlier style of routing, called convention-based routing, is still fully supported.
I just tested this and it seems to work ok.
Url.Action("Index", new { id = UrlParameter.Optional })
generates
/Home/Index
This is probably not the answer you're looking for, but using Url.Route instead of Url.Action seems to work:
@Url.RouteUrl("r2")
This generates /Home/Index
Update
Phil Haack recommendeds generating URLs using the route name:
This might seem like a big problem, but the fix is actually very simple. Use names for all your routes and always use the route name when generating URLs. Most of the time, letting routing sort out which route you want to use to generate an URL is really leaving it to chance. When generating an URL, you generally know exactly which route you want to link to, so you might as well specify it by name.
To solve it in this case is quite easy if yo don't care how it's solved.
Change the third link to:
@Url.RouteUrl("R2")
which gives me:
Why it happends in the first case is something I have yet to figure out. I have been bitten by this too but mostly when it comes to forms.
UPDATE 1:
I was digging around the MVC source and created a few tests that reproduced this problem. The problem seems to be when
RouteCollection.GetVirtualPath(requestContext, correctedValues);
is run. It creates a querystring with the old id. Exactly why I couldn't tell as that class isn't located in the MVC source.
Because r2 does not define the id parameter to be a part of the URL? If you change it to {controller}/{action}/{id}, it will be fine. You'll see the same behaviour if you change r2 to not include action: action will then also becoe a query variable.
The fact that you explicitly set it to "" means that routing will take it into account anyway. Just don't add id and you'll be fine when generating a link.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With