Ответ 1
Try:
// ChangeEventsController
[HttpGet("Create/{id}")]
public IActionResult Create(Guid id)
// ProductsController
[HttpGet("CreateChangeEvent/{id}")]
public IActionResult CreateChangeEvent(Guid id)
Я создаю веб-сайт с использованием ASP.NET Core MVC. Когда я нажимаю на действие, я получаю эту ошибку:
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
Web.Controllers.ChangeEventsController.Create (Web)
Web.Controllers.ProductsController.CreateChangeEvent (Web)
Вот как я определил свое действие в index.cshtmlm для моего ProductsController:
<a asp-controller="ChangeEvents" asp-action="Create" asp-route-id="@item.Id">Create Change Event</a>
Вот моя маршрутизация:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Вот как я определил действия:
// ChangeEventsController
[HttpGet("{id}")]
public IActionResult Create(Guid id)
// ProductsController
[HttpGet("{id}")]
public IActionResult CreateChangeEvent(Guid id)
Что я сделал неправильно?
Обновление
Спасибо @MegaTron за ваш ответ, однако я хотел бы знать, почему у меня не может быть такого же пути действий для разных контроллеров. Я чувствую, что решение, которое вы предложили, не будет хорошо масштабироваться, если у меня будет много контроллеров, каждый из которых создаст объекты.
Try:
// ChangeEventsController
[HttpGet("Create/{id}")]
public IActionResult Create(Guid id)
// ProductsController
[HttpGet("CreateChangeEvent/{id}")]
public IActionResult CreateChangeEvent(Guid id)
Хотя ответ, получивший наибольшее количество голосов, действительно решает проблему, как упоминалось @B12Toaster, он нарушает правила REST. Своим ответом я постараюсь решить проблему, оставаясь при этом RESTful.
TLDR: добавьте свойство Name в свой атрибут HTTP-глагола (GET или иным образом)
Чтобы оба GET работали на обоих контроллерах, сделайте следующее:
// ChangeEventsController
[HttpGet(Name = "Get an event")]
[Route("{id}")]
public IActionResult Create(Guid id)
// ProductsController
[HttpGet(Name = "Get a product")]
[Route("{id}")]
public IActionResult CreateChangeEvent(Guid id)
Этот ответ объясняет, почему нельзя использовать два пути с одним и тем же именем на двух разных контроллерах в Web API. Вы можете реализовать решение, описанное в ответе, чтобы избежать этой проблемы, или использовать ServiceStack, который я лично рекомендовал бы.
Длинный ответ: объяснение того, как быть RESTful в веб-API
Во-первых, давайте сосредоточимся на именах контроллеров. Имена контроллеров должны быть только во множественном числе и существительными. Это привело бы к этим двум контроллерам:
Пояснения по стандартам именования RESTful
Второе: конечные точки в контроллере должны называться операциями CRUD в отношении стандартов RESTful.
Это вместо Create и CreateChangeEvent. Это поможет вам определить, какие глаголы вы вызываете. Для операций не нужно настраивать именование, поскольку в каждом контроллере не должно быть слишком много начальных значений.
Третье: у ваших маршрутов не должно быть пользовательских имен для каждого. Опять же, придерживаясь имен наших методов, они должны быть только операциями CRUD.
В этом случае:
// EventsController
[HttpGet(Name = "Get an event")]
[Route("events/{id}")]
public IActionResult Get(Guid id)
// ProductsController
[HttpGet(Name = "Get a product")]
[Route("products/{id}")]
public IActionResult Get(Guid id)
Это приведет к:
Последнее: для GET HTTP-вызовов вы должны отправлять свои данные с помощью запроса, а не тела. Только PUT/POST/PATCH должен отправлять представление через тело. Это часть ограничений Роя Филдинга в REST. Если вы хотите узнать больше, посмотрите здесь и здесь.
Вы можете сделать это, добавив атрибут [FromQuery] перед каждым из параметров.
// EventsController
[HttpGet(Name = "Get an event")]
[Route("events/{id}")]
public IActionResult Get([FromQuery] Guid id)
// ProductsController
[HttpGet(Name = "Get a product")]
[Route("products/{id}")]
public IActionResult Get([FromQuery] Guid id)
Я надеюсь, что это будет полезно для будущих читателей.
Добавьте атрибут [Route("api/[controller]")]
над каждым из ваших контроллеров, чтобы иметь маршруты действий под разными путями, тогда вы можете использовать один и тот же [HttpGet("{id}")]
в каждом контроллере. Это должно масштабироваться довольно хорошо. См. Этот пример в документах Microsoft.
Если вы не используете аннотацию [Route]
для указания маршрута для каждого контроллера, ваш ASP.NET Core MVC не сможет узнать из коробки, какое действие выбрать для обработки запроса.
Если вы хотите использовать маршрутизацию по умолчанию, следуйте за дульным инструментом:
[Route("[controller]")]
сверху контроллера 'ChangeEvents' (если существует).HttpGet
summery, попробуйте следующее:
// ChangeEventsController
[HttpGet]
public IActionResult Create(Guid id)
// ProductsController
[HttpGet]
public IActionResult CreateChangeEvent(Guid id)
Используйте маршрут, чтобы избежать неоднозначных метаданных в ASP.NET. Измените свой код на этот
// ChangeEventsController
[HttpGet("{id}")]
public IActionResult Create(Guid id)
// ProductsController
[HttpGet]
[Route("[action]/{id}")]
public IActionResult CreateChangeEvent(Guid id)