web-dev-qa-db-ja.com

AmbiguousActionException:複数のアクションが一致しました。次のアクションはルートデータと一致し、すべての制約が満たされました

ASP.NET Core MVCを使用してWebサイトを作成しています。アクションをクリックすると、次のエラーが表示されます。

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)

これは、ProductsControllerのindex.cshtmlmでアクションを定義する方法です。

<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にご連絡いただきありがとうございます。ただし、異なるコントローラーに対して同じアクションパスを使用できない理由を知りたいと思います。それぞれがエンティティを作成する多くのコントローラーがある場合、あなたが提案したソリューションはうまくスケールしないと思います。

18
Zeus82

試してください:

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

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

@ 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の2つの異なるコントローラーで同じ名前の2つのパスを使用できない理由を説明しています。この問題を回避するには、回答で説明したソリューションを実装するか、個人的に推奨する ServiceStack を使用します。


長答:Web API内でRESTfulになる方法の説明

まず、コントローラー名に焦点を当てましょう。コントローラー名は複数形と名詞のみにする必要があります。その結果、次の2つのコントローラーが作成されます。

  • イベント:ChangeEventsの代わり。変更は、コントローラー名としてではなく、PUT内で発生する可能性があります。
  • 製品

RESTful命名規則の説明


2番目:コントローラー内のエンドポイントは、RESTful標準に関してCRUD操作として名前を付ける必要があります。

  • POST
  • GET
  • プット
  • 削除
  • パッチ:オプション

これはCreateおよびCreateChangeEventの代わりです。これにより、呼び出している動詞を見つけることができます。そもそも各コントローラーで開始するのが多すぎてはならないため、操作にカスタムの名前を付ける必要はありません。


3番目:ルートにはnotそれぞれにカスタム名が必要です。繰り返しますが、メソッド名にこだわるのは、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)

これは次の結果になります。

  • / events/{id}のGET
  • / products/{id}のGET

最後:GET HTTP呼び出しの場合は、本文ではなくクエリを使用して入力を送信する必要があります。 PUT/POST/PATCHのみがボディを介して表現を送信する必要があります。これは、RESTのRoy Fieldings制約の一部です。さらに詳しく知りたい場合は、 here および here をご覧ください。

これを行うには、各パラメーターの前に[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)

これが将来の読者に役立つことを願っています。

8
AzzamAziz

デフォルトのルーティングを使用する場合は、blew instrumentに従ってください。

  1. 「ChangeEvents」コントローラーの上部から[Route("[controller]")]を削除します(存在する場合)。
  2. HttpGetからルーティングパターンを削除します

夏らしい、これを試してください:

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

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

ルートを使用して、ASP.NETのあいまいなメソッドを回避します。コードをこれに変更します

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

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

コントローラーのそれぞれの上に[Route("api/[controller]")]属性を追加して、異なるパスの下にアクションをルーティングします。その後、各コントローラーで同じ[HttpGet("{id}")]を使用できます。これは非常にうまくスケーリングするはずです。 Microsoftドキュメントの this の例を参照してください。

[Route]注釈を使用して各コントローラーのルートを指定しない場合、ASP.NET Core MVCは要求を処理するために選択したアクションをすぐに知ることができません。

0
B12Toaster