MVC 6 中的路由
Routing in MVC 6
我有一个超级简单的控制器,有两种方法:
public IActionResult Users(long id)
{
return Json(new { name = "Example User" });
}
public IActionResult Users()
{
return Json(new { list = new List<User>() });
}
一个发送给 select 所有用户,另一个发送给 return 所有用户。在网络 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
我做错了什么?
在您原来的 webapi 代码中,您使用 Routes.MapHttpRoute
添加 webapi 特定路由。这与 MVC 路由不同,后者不会考虑操作中的参数,例如,如果您使用 Routes.MapRoute
.
,您在 MVC 5 中会遇到同样的问题
您的 MVC 6 代码中也发生了同样的事情,因为您正在使用 routes.MapRoute
添加标准 MVC 路由。在这两种情况下,框架都找到了 2 个控制器操作,它们匹配相同的路由,没有额外的约束。它需要一些帮助才能 select 这 2 个操作之一。
消除 api 操作歧义的最简单方法是使用属性路由而不是定义路由,如 this example:
[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 控制器所做的工作。您可以查看添加的约定 here. Check this guide 以快速了解此包。
我有一个超级简单的控制器,有两种方法:
public IActionResult Users(long id)
{
return Json(new { name = "Example User" });
}
public IActionResult Users()
{
return Json(new { list = new List<User>() });
}
一个发送给 select 所有用户,另一个发送给 return 所有用户。在网络 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
我做错了什么?
在您原来的 webapi 代码中,您使用 Routes.MapHttpRoute
添加 webapi 特定路由。这与 MVC 路由不同,后者不会考虑操作中的参数,例如,如果您使用 Routes.MapRoute
.
您的 MVC 6 代码中也发生了同样的事情,因为您正在使用 routes.MapRoute
添加标准 MVC 路由。在这两种情况下,框架都找到了 2 个控制器操作,它们匹配相同的路由,没有额外的约束。它需要一些帮助才能 select 这 2 个操作之一。
消除 api 操作歧义的最简单方法是使用属性路由而不是定义路由,如 this example:
[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 控制器所做的工作。您可以查看添加的约定 here. Check this guide 以快速了解此包。