web-dev-qa-db-fra.com

AmbiguousActionException: plusieurs actions correspondantes. Les actions suivantes correspondaient aux données de route et toutes les contraintes étaient satisfaites

Je crée un site Web à l'aide d'ASP.NET Core MVC. Lorsque je clique sur une action, j'obtiens cette erreur:

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)

Voici comment j'ai défini mon action dans le fichier index.cshtmlm pour ProductsController:

<a asp-controller="ChangeEvents" asp-action="Create" asp-route-id="@item.Id">Create Change Event</a>

Voici mon routage:

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

Voici comment j'ai défini les actions:

// ChangeEventsController
[HttpGet("{id}")]
public IActionResult Create(Guid id)

// ProductsController
[HttpGet("{id}")]
public IActionResult CreateChangeEvent(Guid id)

Qu'est ce que j'ai mal fait?

Mettre à jour

Merci @ MegaTron pour votre réponse, mais j'aimerais savoir pourquoi je ne peux pas avoir le même chemin d'action pour différents contrôleurs J'ai l'impression que la solution que vous proposez ne sera pas adaptée si j'ai plusieurs contrôleurs qui créent chacun des entités. 

15
Zeus82

Essayer:

// ChangeEventsController
[HttpGet("Create/{id}")]
public IActionResult Create(Guid id)

// ProductsController
[HttpGet("CreateChangeEvent/{id}")]
public IActionResult CreateChangeEvent(Guid id)
25
Roman Marusyk

Bien que la réponse la plus votée résolve le problème, comme mentionné par @ B12Toaster, cela violerait les règles de REST. Avec ma réponse, je vais essayer de résoudre le problème tout en restant RESTful.


TLDR: ajoute la propriété Name à votre attribut de verbe HTTP (GET ou autre)

Pour que les deux méthodes GET fonctionnent dans les deux contrôleurs, procédez comme suit:

// 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)

Cette réponse explique pourquoi vous ne pouvez pas avoir deux chemins portant le même nom sur deux contrôleurs différents dans l'API Web. Vous pouvez implémenter la solution décrite dans la réponse pour éviter ce problème ou utiliser ServiceStack que je recommanderais personnellement.


Réponse longue: Expliquer comment être RESTful dans une API Web

Premièrement: concentrons-nous sur les noms des contrôleurs. Les noms de contrôleur doivent être au pluriel et les noms uniquement. Cela donnerait ces deux contrôleurs:

  • Événements: au lieu de ChangeEvents. Le changement peut se produire dans un PUT, pas sous le nom du contrôleur.
  • Des produits

Explication sur les normes de nommage RESTful


Deuxièmement: les points de terminaison d’un contrôleur doivent être nommés opérations CRUD par rapport aux normes RESTful.

  • POST
  • GET
  • METTRE
  • EFFACER
  • PATCH: facultatif

Ceci est à la place de Create et CreateChangeEvent. Cela vous aide à localiser les verbes que vous appelez. Il n’est pas nécessaire de nommer de manière personnalisée les opérations car il ne devrait pas y en avoir trop en premier lieu pour commencer dans chaque contrôleur.


Troisièmement: Vos routes devraient pas avoir des noms personnalisés pour chacun. Encore une fois, en nous en tenant à nos noms de méthodes, elles ne devraient être que des opérations CRUD.

Dans ce cas:

// 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)

Cela se traduirait par:

  • GET for/events/{id}
  • GET pour/produits/{id}

Dernier: pour les appels HTTP GET, vous devez envoyer votre entrée via une requête plutôt que du corps. Seul PUT/POST/PATCH doit envoyer une représentation via un organe. Cela fait partie des contraintes de Roy Fieldings dans REST. Si vous voulez savoir plus loin, regardez ici et ici .

Vous pouvez le faire en ajoutant l'attribut [FromQuery] avant chacun des paramètres.

// 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)

J'espère que cela serait utile aux futurs lecteurs.

3
AzzamAziz

Si vous souhaitez utiliser le routage par défaut, suivez les instructions suivantes:

  1. Supprimez [Route("[controller]")] du haut du contrôleur 'ChangeEvents' (s'il existe).
  2. Supprimer le modèle de routage de HttpGet

en été, essayez ceci:

// ChangeEventsController
[HttpGet]
public IActionResult Create(Guid id)

// ProductsController
[HttpGet]
public IActionResult CreateChangeEvent(Guid id)
0
D.L.MAN

Ajoutez l'attribut [Route("api/[controller]")] au-dessus de chacun de vos contrôleurs pour que les itinéraires d'action soient sous des chemins différents. Vous pouvez ensuite utiliser le même [HttpGet("{id}")] dans chaque contrôleur. Cela devrait plutôt bien évoluer. Voir this example dans les documents Microsoft.

Si vous n'utilisez pas l'annotation [Route] pour spécifier une route pour chaque contrôleur, votre ASP.NET Core MVC ne peut pas savoir immédiatement quelle action choisir pour gérer la demande.

0
B12Toaster