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にご連絡いただきありがとうございます。ただし、異なるコントローラーに対して同じアクションパスを使用できない理由を知りたいと思います。それぞれがエンティティを作成する多くのコントローラーがある場合、あなたが提案したソリューションはうまくスケールしないと思います。
試してください:
// 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の2つの異なるコントローラーで同じ名前の2つのパスを使用できない理由を説明しています。この問題を回避するには、回答で説明したソリューションを実装するか、個人的に推奨する ServiceStack を使用します。
長答:Web API内でRESTfulになる方法の説明
まず、コントローラー名に焦点を当てましょう。コントローラー名は複数形と名詞のみにする必要があります。その結果、次の2つのコントローラーが作成されます。
2番目:コントローラー内のエンドポイントは、RESTful標準に関してCRUD操作として名前を付ける必要があります。
これは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)
これは次の結果になります。
最後: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)
これが将来の読者に役立つことを願っています。
デフォルトのルーティングを使用する場合は、blew instrumentに従ってください。
[Route("[controller]")]
を削除します(存在する場合)。HttpGet
からルーティングパターンを削除します夏らしい、これを試してください:
// 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)
コントローラーのそれぞれの上に[Route("api/[controller]")]
属性を追加して、異なるパスの下にアクションをルーティングします。その後、各コントローラーで同じ[HttpGet("{id}")]
を使用できます。これは非常にうまくスケーリングするはずです。 Microsoftドキュメントの this の例を参照してください。
[Route]
注釈を使用して各コントローラーのルートを指定しない場合、ASP.NET Core MVCは要求を処理するために選択したアクションをすぐに知ることができません。