0

I have the following routes in my controller. One Get and two post actions. All have the same action name. The two post actions are differentiated using the MultipleButton attribute as explained here:

[RoutePrefix("incidents")]
public sealed class IncidentsController : Controller
{    
    [HttpGet, Route("action/{id:int?}/{error?}")]
    public async Task<ActionResult> Action(int? id, string error = null)

    [HttpPost, Route("action"), ActionName("Action"), ValidateAntiForgeryToken]
    [MultipleButton(Name = "action", Argument = "Accept")]
    public async Task<ActionResult> ActionAccept(IncidentActionViewModel incident)

    [HttpPost, Route("action"), ActionName("Action"), ValidateAntiForgeryToken]
    [MultipleButton(Name = "action", Argument = "Reject")]
    public async Task<ActionResult> ActionReject(IncidentActionViewModel incident)
}

@Url.Action("Action", "Incidents", new { id = 10 })

The above route gets rendered as shown below. Navigating to this URL works but if I change the 'id' parameter from Nullable to int I start to get errors.

/incidents/action?id=10

It should be rendering as shown below and I should not get errors if I change the 'id' parameter to type int:

/incidents/action/10

What am I doing wrong?

UPDATE

Here are my route registration details:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapMvcAttributeRoutes();

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Community
  • 1
  • 1
Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311

1 Answers1

0

As a work around, I found that I could use named routes instead of relying on ASP.NET MVC to resolve the routes using the controller and action names. So I ended up with the following code:

[RoutePrefix("incidents")]
public sealed class IncidentsController : Controller
{    
    [HttpGet, Route("action/{id:int?}/{error?}", Name = "IncidentsGetAction")]
    public async Task<ActionResult> Action(int? id, string error = null)

    [HttpPost, Route("action", Name = "IncidentsPostActionAccept"), ActionName("Action"), ValidateAntiForgeryToken]
    [MultipleButton(Name = "action", Argument = "Accept")]
    public async Task<ActionResult> ActionAccept(IncidentActionViewModel incident)

    [HttpPost, Route("action", Name = "IncidentsPostActionReject"), ActionName("Action"), ValidateAntiForgeryToken]
    [MultipleButton(Name = "action", Argument = "Reject")]
    public async Task<ActionResult> ActionReject(IncidentActionViewModel incident)
}

// Generate a URL
@Url.RouteUrl("IncidentsGetAction", new { id = 10 })
// Generate a link to a URL
@Html.RouteLink("Link Text", "IncidentsGetAction", new { id = 10 })

This approach has better performance as, MVC does not have to resolve the URL, it can go to it directly. It is also easier to understand and reduces the chances of bugs arising as you are being explicit about the route you want to use.

I personally have gone as far as converting all my routes to named routes and have abandoned using controller and action resolved routing. It would still be interesting to see why the above problem occurs.

Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311