【问题标题】:Why are these two Actions considered ambiguous despite the parameters being different?尽管参数不同,为什么这两个动作被认为是模棱两可的?
【发布时间】:2011-10-04 13:36:52
【问题描述】:

当前对控制器类型“UsersController”的操作“Index”的请求在以下操作方法之间不明确: System.Web.Mvc.ActionResult PostUser(Models.SimpleUser) 类型为 Controllers.UsersController System.Web.Mvc.ActionResult PostUser(Int32, Models.SimpleUser) on type Controllers.UsersController

当我尝试使用表单值发布 website.com/users/ 时发生。

当没有 ID (website.com/users/) 我希望它创建一个新用户时,当有一个 ID 时(例如 /users/51)我希望它更新该用户,那么我该如何让它区分这两个动作?

这是两个动作:

    [HttpPost]
    [ActionName("Index")]
    public ActionResult PostUser(SimpleUser u)
    {

        return Content("User to be created...");
    }

    [HttpPost]
    [ActionName("Index")]
    public ActionResult PostUser(int id, SimpleUser u)
    {

        return Content("User to be updated...");
    }

这是地图路线:

    routes.MapRoute(
        "Users",
        "users/{id}",
        new { controller = "Users", action = "Index" }
    );

【问题讨论】:

标签: asp.net-mvc action asp.net-mvc-routing


【解决方案1】:

您必须通过使用两个不同的操作名称来消除方法的歧义。

要拥有 RESTful API,您可以创建自己的 RouteHandler 类,该类将根据 {id} 值是否存在而改变路由。 RouteHandler 类很简单:

using System.Web.Mvc;
using System.Web.Routing;

  public class MyRouteHandler : IRouteHandler
  {
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
      var routeData = requestContext.RouteData;
      if (routeData.Values("id").ToString() == "0" /* our default invalid value */ )
      {
        var action = routeData.Values("action");
        routeData.Values("action") = action + "Add";
      }
      var handler = new MvcHandler(requestContext);
      return handler;
    }
  }

接下来,修改Global.asax.cs中的RegisterRoutes()如下:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        AreaRegistration.RegisterAllAreas();

        routes.Add("Default", new Route("{controller}/{action}/{id}",
            new RouteValueDictionary(
                new { controller = "Home", 
                      action = "Index", 
                      id = "0" /* our default invalid value */ }),
            new MyRouteHandler()));
    }

最后,将 Add 方法的 ActionName 属性更改为“IndexAdd”。当请求没有 id 时,将提供默认的无效值,这会提醒您的 RouteHandler 将方法更改为您的“添加”方法。

辅导员

【讨论】:

    【解决方案2】:

    主要问题是您的两种操作方法之间存在一些歧义,因为模型绑定无助于确定路由配置。您当前的路由只是指向 index 方法。

    您仍然可以使用相同的 URL,但是以不同的方式命名您的操作然后应用一些新路由会很有帮助。

    [HttpPost]
    public ActionResult Create(SimpleUser u)
    {
        return Content("User to be created...");
    }
    
    [HttpPost]
    public ActionResult Edit(int id, SimpleUser u)
    {
        return Content("User to be updated...");
    }
    

    然后在你的路由中尝试

    routes.MapRoute(
        "UserEdit",
        "users/{id}",
        new { controller = "Users", action = "Edit", 
            httpMethod = new HttpMethodConstraint("POST") });
    
    routes.MapRoute(
        "UserCreate",
        "users",
        new { controller = "Users", action = "Create", 
            httpMethod = new HttpMethodConstraint("POST") });
    

    路由将仅限于 POST 事件,这意味着您仍然可以在同一路由上为您的 GET 方法添加一些路由。比如列表什么的。

    【讨论】:

    • 我很好奇为什么它没有检测到 /{id} 并因此选择了接受 id 的 Action。我想我很困惑,因为在我使用的大多数语言中,参数列表与名称一样是方法签名的一部分。
    • 嗯,这就是开箱即用的 ASP.NET MVC 处理为每个请求查找操作的方式。最后,它归结为寻找与您正在调用的操作具有匹配别名的方法,并且与该方法关联的所有属性都返回 true。它实际上并没有将每个方法的参数视为确定选择哪个操作的方法。因此,当多个方法具有相同的别名并且所有属性都返回 true 时,框架不知道要调用哪个方法。您可以通过查看 MVC 源代码中的 ControllerActionInvoker.cs 类来深入挖掘。
    【解决方案3】:

    如果您考虑使用创建和编辑操作来实现您想要的效果会更好。通常 index 可用于列出所有用户(在您的情况下)。 关于您面临的问题,它是因为没有不以任何 id 作为参数的路由而出现的。

    【讨论】:

    • 不能那样做,我正在使用必要的幂等 URL 创建一个 RESTful API,GET/PUT/DELETE 都将使用相同的 /users/[id] URL,POST 将使用 /users/ (没有 ID),实际上我现在也在使用 POST 进行创作,但你现在明白了。此外,这可能是比答案更好的评论,因为它没有回答问题。
    猜你喜欢
    • 2019-11-28
    • 2018-05-28
    • 1970-01-01
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多