AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied
我正在使用ASP.NET Core MVC创建一个网站。 当我点击某个动作时,我收到此错误:
1 2 3 4 | 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中定义我的动作的方法:
1 | Create Change Event |
这是我的路线:
1 2 3 4 5 6 | app.UseMvc(routes => { routes.MapRoute( name:"default", template:"{controller=Home}/{action=Index}/{id?}"); }); |
以下是我定义操作的方式:
1 2 3 4 5 6 7 | // ChangeEventsController [HttpGet("{id}")] public IActionResult Create(Guid id) // ProductsController [HttpGet("{id}")] public IActionResult CreateChangeEvent(Guid id) |
我做错了什么?
更新
感谢@MegaTron的回复,但是我想知道为什么我不能为不同的控制器提供相同的操作路径。 如果我有许多控制器,每个创建实体,我觉得你提出的解决方案不会很好地扩展。
尝试:
1 2 3 4 5 6 7 | // 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,请执行以下操作:
1 2 3 4 5 6 7 8 9 | // 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命名标准的说明
第二:控制器中的端点应该被命名为与RESTful标准相关的CRUD操作。
- POST
- 得到
- 放
- 删除
- 补丁:可选
这不是Create和CreateChangeEvent。这有助于您找到要调用的动词。不需要为操作定制命名,因为在每个控制器中首先应该没有太多的开始。
第三:您的路线不应该为每个路线都有自定义名称。再次,坚持我们的方法名称,它们应该只是CRUD操作。
在这种情况下:
1 2 3 4 5 6 7 8 9 | // 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应该通过正文发送一个表示。这是Roy Fieldings在REST中的限制的一部分。如果你想进一步了解,请看这里和这里。
您可以通过在每个参数之前添加[FromQuery]属性来完成此操作。
1 2 3 4 5 6 7 8 9 | // 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) |
我希望这对未来的读者有所帮助。
使用路由来避免ASP.NET中含糊不清的方法。将您的代码更改为此
1 2 3 4 5 6 7 8 | // ChangeEventsController [HttpGet("{id}")] public IActionResult Create(Guid id) // ProductsController [HttpGet] [Route("[action]/{id}")] public IActionResult CreateChangeEvent(Guid id) |
如果要使用默认路由,请按照吹动仪器进行操作:
夏天,试试这个:
1 2 3 4 5 6 7 | // ChangeEventsController [HttpGet] public IActionResult Create(Guid id) // ProductsController [HttpGet] public IActionResult CreateChangeEvent(Guid id) |
在每个控制器上方添加
如果不使用