22

我正在使用 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)

这就是我在我的 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 的回复,但是我想知道为什么我不能为不同的控制器提供相同的操作路径。如果我有许多控制器每个都创建实体,我觉得你提出的解决方案无法很好地扩展。

4

5 回答 5

31

尝试:

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

// ProductsController
[HttpGet("CreateChangeEvent/{id}")]
public IActionResult CreateChangeEvent(Guid id)
于 2016-11-24T23:29:29.130 回答
16

虽然投票最多的答案确实解决了这个问题,但正如@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 。


长答案:解释如何在 Web API 中实现 RESTful

首先:让我们关注控制器名称。控制器名称应该是复数和名词。这将导致这两个控制器:

  • 事件:而不是 ChangeEvents。更改可能发生在 PUT 中,而不是控制器名称。
  • 产品

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)

这将导致:

  • 获取 /events/{id}
  • 获取 /products/{id}

最后:对于 GET HTTP 调用,您应该通过查询而不是正文发送输入。只有 PUT/POST/PATCH 应该通过正文发送表示。这是 REST 中 Roy Fieldings 约束的一部分。如果您想进一步了解,请查看此处此处

您可以通过在每个参数之前添加 [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)

我希望这对未来的读者有所帮助。

于 2018-11-13T05:02:59.083 回答
2

如果要使用默认路由,请按照自爆工具:

  1. 从“ChangeEvents”控制器顶部移除[Route("[controller]")](如果存在)。
  2. 删除路由模式HttpGet

夏天,试试这个:

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

// ProductsController
[HttpGet]
public IActionResult CreateChangeEvent(Guid id)
于 2018-12-07T19:54:50.963 回答
0

在 ASP.NET 中使用路由来避免模棱两可的方法。将您的代码更改为此

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

// ProductsController
[HttpGet]
[Route("[action]/{id}")]
public IActionResult CreateChangeEvent(Guid id)
于 2019-05-21T10:38:27.693 回答
-1

在每个控制器上方添加[Route("api/[controller]")]属性以使动作路由在不同的路径下,然后您可以[HttpGet("{id}")]在每个控制器中使用相同的路径。这应该可以很好地扩展。请参阅microsoft 文档中的示例。

如果您不使用[Route]注解为每个控制器指定路由,则您的 ASP.NET Core MVC 无法立即知道选择哪个操作来处​​理请求。

于 2018-03-19T21:07:00.613 回答