在MVC中路由6

我有一个超级简单的控制器,有两种方法:

public IActionResult Users(long id) { return Json(new { name = "Example User" }); } public IActionResult Users() { return Json(new { list = new List() }); } 

一个用于选择所有用户,另一个用于返回所有用户。 在web api 2中,我可以使用以下路由,一切正常:

 config.Routes.MapHttpRoute( name: "Users", routeTemplate: "v1/Users", defaults: new { action = "Users", controller = "Users" }, constraints: null, handler: new TokenValidationHandler() { InnerHandler = new HttpControllerDispatcher(config) } ); 

我在startup.cs中设置了以下路由:

 app.UseMvc(routes => { routes.MapRoute(name: "User_Default", template: "v1/{controller=Users}/{action=Users}/{id?}"); }); 

但是这给了我一个AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied

我究竟做错了什么?

在您的原始webapi代码中,您使用的是Routes.MapHttpRoute ,它添加了特定于webapi的路由。 这与MVC路由不同,MVC路由不考虑操作中的参数,例如,如果您使用Routes.MapRoute则在MVC 5中会遇到相同的问题。

您的MVC 6代码也会发生同样的事情,因为您正在使用routes.MapRoute添加标准MVC路由。 在这两种情况下,框架都找到了匹配相同路由的2个控制器动作,没有其他约束。 它需要一些帮助才能选择其中一个动作。

消除api操作歧义的最简单方法是使用属性路由而不是定义路由,如下例所示 :

 [Route("v1/[controller]")] public class UsersController : Controller { [HttpGet("{id:int}")] public IActionResult Users(long id) { return Json(new { name = "Example User" }); } public IActionResult Users() { return Json(new { list = new[] { "a", "b" } }); } } 

还有其他选项可以让您更改MVC 6中MVC路由的行为。您可以创建自己的IActionConstraint属性来强制执行是否具有给定参数。 这样,其中一个动作需要路由中的id参数,而另一个动作需要没有id参数(警告,未经测试的代码):

 public class UsersController : Controller { [RouteParameterConstraint("id", ShouldAppear=true)] public IActionResult Users(long id) { return Json(new { name = "Example User" }); } [RouteParameterConstraint("id", ShouldNotAppear=true)] public IActionResult Users() { return Json(new { list = new[] { "a", "b" } }); } } public class RouteParameterConstraintAttribute : Attribute, IActionConstraint { private routeParameterName; public RouteParameterConstraintAttribute(string routeParameterName) { this.routerParamterName = routerParameterName; } public int Order => 0; public bool ShouldAppear {get; set;} public bool ShouldNotAppear {get; set;} public bool Accept(ActionConstraintContext context) { if(ShouldAppear) return context.RouteContext.RouteData.Values["country"] != null; if(ShouldNotAppear) return context.RouteContext.RouteData.Values["country"] == null; return true; } } 

处理webapi 2样式控制器的更好选择是在MVC管道中添加约定。 这正是Microsoft.AspNet.Mvc.WebApiCompatShim为帮助迁移webapi 2控制器所做的工作。 您可以在此处查看约定。 查看本指南以快速了解此软件包。