Switching to {controller}/{id}/{action} breaks Red

2020-03-01 19:24发布

问题:

I am trying to use proper REST urls with MVC. To do that I switched default Routing from:

{controller}/{action}/{id}

to

{controller}/{id}/{action}

so instead of:

/Customer/Approve/23

there is now

/Customer/23/Approve

ActionLink seems to work ok, but the following code in CustomerController:

[CustomAuthorize]
[HttpGet]
public ActionResult Approve(int id)
{
    _customerService.Approve(id);
    return RedirectToAction("Search");  //Goes to bad url
}

ends up on url /Customer/23/Search. While it should be going to /Customer/Search. Somehow it remembers 23 (id).

Here is my routing code in global.cs

    routes.MapRoute(
        "AdminRoute", // Route name
        "{controller}/{id}/{action}", 
        new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        new { id = new IsIntegerConstraint() }
        );

    routes.MapRoute(
        "Default", 
        "{controller}/{action}", 
        new { controller = "Home", action = "Index" });

If I switch the two functions, RedirectToAction starts working, but using:

Html.ActionLink("Approve", "Approve", new { Id = 23})

Now generates /Customer/Approve?id=23, instead of /Customer/23/Approve.

I could specify direct urls like ~/Customer/23/Approve, instead of using ActionLink and RedirectToAction, but would rather stick to functions provided by MVC.

回答1:

When you use RedirectToAction(), internally, MVC will take the existing route data (including the Id value) to build the url. Even if you pass a null RouteValueDictionary, the existing route data will be merged with the new empty route value data.

The only way around this I can see is to use RedirectToRoute(), as follows:

return RedirectToRoute("Default", new { controller = "Customer", action = "Search"});

counsellorben



回答2:

Try passing in new (empty) RouteValueDictionary in your controller

return RedirectToAction("Search", new System.Web.Routing.RouteValueDictionary{});

And here:

Html.ActionLink("Approve", "Approve", new { Id = 23})

I don't even know how can it pick up the Customer controller, since you are not specifying it anywhere. Try providing both controller and action to ActionLink helper.



回答3:

Try passing the current route data to methon in your controller action:

return RedirectToAction("Search", this.RouteData.Values);


回答4:

Remove this part:

id = UrlParameter.Optional

may be resolve the problem; when you define "id" as an optional parameter, and you have the "Default" map, the "Default" and the "AdminRoute" are same together! regards.



回答5:

I was having a similar problem. Route values that were passed to my controller action were being reused when I tried to redirect the user with RedirectToAction, even if I didn't specify them in the new RouteValueDictionary. The solution that I came up with (after reading counsellorben's post) with was to clear out the RouteData for the current request. That way, I could stop MVC from merging route values that I didn't specify.

So, in your situation maybe you could do something like this:

[CustomAuthorize]
[HttpGet]
public ActionResult Approve(int id)
{
    _customerService.Approve(id);
    this.RouteData.Values.Clear();  //clear out current route values
    return RedirectToAction("Search");  //Goes to bad url
}


回答6:

I had a similar problem and was able to solve it by adding the id to the default route as well.

routes.MapRoute(
    "Default", 
    "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional });

If there is truly no id in your default route then you could also try:

routes.MapRoute(
    "Default", 
    "{controller}/{action}", 
    new { controller = "Home", action = "Index", id = string.Empty });