MVC 6 中的路由

本文关键字:路由 MVC | 更新日期: 2023-09-27 17:57:13

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

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

一个用于选择所有用户,另一个用于返回所有用户。在 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) }
            );

我在启动时设置了以下路由.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

我做错了什么?

MVC 6 中的路由

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

同样的事情也发生在您的 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 控制器所做的。您可以看到此处添加的约定。查看本指南,快速了解此软件包。