[NOTE: I'm using ASP.NET MVC2 RC2.]
I have URLs like this:
/customers/123/orders/456/items/index
/customers/123/orders/456/items/789/edit
My routing table lists the most-specific routes first, so I've got:
// customers/123/orders/456/items/789/edit
routes.MapRoute(
"item", // Route name
"customers/{customerId}/orders/{orderId}/items/{itemId}/{action}", // URL with parameters
new { controller = "Items", action = "Details" }, // Parameter defaults
new { customerId = @"\d+", orderId = @"\d+", itemId = @"\d+" } // Constraints
);
// customers/123/orders/456/items/index
routes.MapRoute(
"items", // Route name
"customers/{customerId}/orders/{orderId}/items/{action}", // URL with parameters
new { controller = "Items", action = "Index" }, // Parameter defaults
new { customerId = @"\d+", orderId = @"\d+" } // Constraints
);
When I'm in the item "Edit" page, I want a link back up to the "Index" page. So, I use:
ActionLink("Back to Index", "index")
However, because there's an ambient item ID, this results in the URL:
/customers/123/orders/456/items/789/index
...whereas I want it to "forget" the item ID and just use:
/customers/123/orders/456/items/index
I've tried overriding the item ID like so:
ActionLink("Back to Index", "index", new { itemId=string.empty开发者_如何转开发 })
...but that doesn't quite work. What that gives me is:
/customers/123/orders/456/items?itemId=789
How can I persuade ActionLink
to "forget" the item ID?
EDIT: fixed question - I was referring to "order" where I meant "item".
EDIT: added detail of why itemId=string.empty doesn't work.
In your constraint you can delete any routeValues you do not need.
values.Remove("itemId");
Then create a class that inherits from IRouteConstraint.
Then in the Match method clear your route value and return true (indicates the route matches).
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
values.Remove("itemId");
return true;
}
then in Global.asax.cs change
new { customerId = @"\d+", orderId = @"\d+" }
to
new { customerId = @"\d+", orderId = @"\d+", custom=new MyNewConstraint() }
I deleted my previous answer, didn't notice they were the same controller. Try setting itemId = string.empty. This will make the first route fail, as it requires a digit in that position.
ActionLink("Back to Index", "index", new { itemId=string.empty })
Optionally you can create a link with the name of the route "item" manually
<%= Url.RouteUrl("item", new { controller="Items", action="Index", orderId=3 ... }) %>
OK, I think I've got this to do what I want, by using RouteLink instead of ActionLink:
RouteLink("Back to Index", "items", new { action="index" })
...where the 2nd parameter is the name of the route I want to explicitly invoke.
EDIT: I didn't really want to go through all my dozens of ActionLinks
and put them in this form, so instead I've modified the "item" rule to include a constraint on the {action}
parameter, so that "index" does not match this route. Thus, it matches the "items" route instead - which has no itemId
parameter. That seems to be a simpler way to do this, since the bulk of my code is unchanged.
Solution at the root of the problem
It seems that the optimal solution (that doesn't smell like a workaround) is the one that solves the problem where it has roots and that's in routing.
I've written a custom Route
class called RouteWithExclusions
that is able to define route value names that should be excluded/removed when generating URLs.
The whole problem is detailed and explained in my blog post and all the code is provided there as well. Check it out, it may help you solve this routing problem without the use of constraints but with a route that does what's required.
精彩评论