Friday, August 18, 2017

MVC Controller - Disambiguating actions

When two actions match through routing, MVC must disambiguate to choose the 'best' candidate or else throw an exception. For example:
public class ProductsController : Controller{
public IActionResult Edit(int id) { ... }
[HttpPost]
public IActionResult Edit(int id, Product product) { ... }
}

This controller defines two actions that would match the URL path /Products/Edit/17 and route data { controller = Products, action = Edit, id = 17 }. This is a typical pattern for MVC controllers where Edit(int) shows a form to edit a product, and Edit(int, Product) processes the posted form. To make this possible MVC would need to choose Edit(int, Product) when the request is an HTTP POST and Edit(int) when the HTTP verb is anything else.



The HttpPostAttribute ( [HttpPost] ) is an implementation of IActionConstraint that will only allow the action to be selected when the HTTP verb is POST. The presence of an IActionConstraint makes the Edit(int, Product) a 'better' match than Edit(int), so Edit(int, Product) will be tried first.

You will only need to write custom IActionConstraint implementations in specialized scenarios, but it's important to understand the role of attributes like HttpPostAttribute - similar attributes are defined for other HTTP verbs. In conventional routing it's common for actions to use the same action name when they are part of a show form -> submit form workflow. The convenience of this pattern will become more apparent after reviewing the Understanding IActionConstraint section.
If multiple routes match, and MVC can't find a 'best' route, it will throw an AmbiguousActionException.