it-swarm.com.de

AmbiguousActionException: Mehrere übereinstimmende Aktionen. Die folgenden Aktionen stimmen mit den Routendaten überein und alle Bedingungen wurden erfüllt

Ich erstelle eine Website mit ASP.NET Core MVC. Wenn ich auf eine Aktion klicke, erhalte ich diese Fehlermeldung:

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)

So habe ich meine Aktion in der index.cshtmlm für meinen ProductsController definiert:

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

Hier ist mein Routing:

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

So habe ich die Aktionen definiert:

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

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

Was habe ich falsch gemacht?

Update

Vielen Dank @MegaTron für Ihre Antwort. Ich würde jedoch gerne wissen, warum ich nicht den gleichen Aktionspfad für verschiedene Controller haben kann. Ich denke, dass die von Ihnen vorgeschlagene Lösung nicht gut skalierbar ist, wenn ich viele Controller habe, die jeweils Entitäten erstellen. 

15
Zeus82

Versuchen:

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

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

Obwohl die am meisten gewählte Antwort das Problem löst, würde dies, wie von @ B12Toaster erwähnt, gegen die Regeln von REST verstoßen. Mit meiner Antwort werde ich versuchen, das Problem zu lösen und dabei RESTful zu bleiben.


TLDR: Fügen Sie Ihrem HTTP-Verbattribut die Name-Eigenschaft hinzu (GET oder anderweitig).

Damit beide GETs in beiden Controllern funktionieren, machen Sie folgendes:

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

Diese Antwort erklärt, warum Sie nicht zwei Pfade mit demselben Namen auf zwei verschiedenen Controllern in der Web-API haben können. Sie können die in der Antwort beschriebene Lösung implementieren, um dieses Problem zu vermeiden, oder Sie können ServiceStack verwenden, das ich persönlich empfehlen würde.


Lange Antwort: Erläutern, wie RESTful innerhalb der Web-API ausgeführt wird

Erstens: Konzentrieren wir uns auf die Controller-Namen. Die Controller-Namen sollten nur Plural und Nomen sein. Das würde zu diesen beiden Controllern führen:

  • Ereignisse: Anstelle von ChangeEvents. Die Änderung kann innerhalb eines PUT erfolgen, nicht als Controller-Name.
  • Produkte

Erklärung zu RESTful-Benennungsstandards


Zweitens: Die Endpunkte innerhalb eines Controllers sollten in Bezug auf RESTful-Standards als CRUD-Operationen bezeichnet werden.

  • POST
  • GET
  • STELLEN
  • LÖSCHEN
  • PATCH: Optional

Dies ist anstelle von Create und CreateChangeEvent. So können Sie herausfinden, welche Verben Sie aufrufen. Es ist keine benutzerdefinierte Benennung für die Vorgänge erforderlich, da es in jedem Controller zunächst einmal zu viele geben sollte.


Drittens: Ihre Routen sollten nicht benutzerdefinierte Namen haben. Wenn Sie sich an unsere Methodennamen halten, sollten sie nur CRUD-Operationen sein.

In diesem Fall:

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

Dies würde zu Folgendem führen:

  • GET für/events/{id}
  • GET für/products/{id}

Last: Bei GET HTTP-Aufrufen sollten Sie Ihre Eingabe eher über die Abfrage als über den Body senden. Nur PUT/POST/PATCH sollte eine Darstellung über body senden. Dies ist Teil der Roy Fieldings-Einschränkungen in REST. Wenn Sie weiter wissen wollen, schauen Sie hier und hier .

Sie können dies tun, indem Sie vor jedem Parameter das [FromQuery] -Attribut hinzufügen.

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

Ich hoffe, das wäre für zukünftige Leser hilfreich.

3
AzzamAziz

Wenn Sie das Standard-Routing verwenden möchten, folgen Sie blew instrument:

  1. Entfernen Sie [Route("[controller]")] vom Controller 'ChangeEvents' (falls vorhanden).
  2. Routingmuster aus HttpGet entfernen

sommerlich, versuche dies:

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

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

Verwenden Sie route, um mehrdeutige Methoden in ASP.NET zu vermeiden. Ändern Sie Ihren Code in diesen

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

// ProductsController
[HttpGet]
[Route("[action]/{id}")]
public IActionResult CreateChangeEvent(Guid id)
0
Rohan Shenoy

Fügen Sie das [Route("api/[controller]")]-Attribut über jedem Controller hinzu, damit die Aktionsrouten unter verschiedenen Pfaden liegen. Dann können Sie in jedem Controller das gleiche [HttpGet("{id}")] verwenden. Dies sollte ziemlich gut skalieren. Siehe this Beispiel in den Microsoft-Dokumenten.

Wenn Sie die [Route]-Anmerkung nicht verwenden, um eine Route für jeden Controller anzugeben, kann Ihr ASP.NET Core-MVC nicht sofort wissen, welche Aktion für die Verarbeitung der Anforderung ausgewählt wird.

0
B12Toaster