【问题标题】:ASP.NET Routing where two controller actions have the same number of parametersASP.NET 路由,其中​​两个控制器操作具有相同数量的参数
【发布时间】:2013-04-23 02:44:45
【问题描述】:

我们有一个这样的路由配置:

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

     routes.MapRoute(
         name: "About",
         url: "{controller}/{action}/{aboutId}",
         defaults: new { controller = "Home", action = "About" }
     );

     routes.MapRoute(
         name: "Contact",
         url: "{controller}/{action}/{contactId}",
         defaults: new { controller = "Home", action = "Contact" }
     );

     routes.MapRoute(
         name: "Default",
         url: "{controller}/{action}/{id}",
         defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
     );
  }

您会注意到有两条路由具有一个强制性的额外参数。路由AboutContact

在我们的应用中,我们有两个网址

www.myapp.com/Home/About/2 工作正常。 但是当我们将浏览器导航到www.myapp.com/Home/Contact/5 时,我们得到了可怕的路由异常:

The parameters dictionary contains a null entry for parameter 'contactId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Contact(Int32)' in 'RoutingTest.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.

如果我们改变路由的顺序,让它看起来像这样:

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

     routes.MapRoute(
         name: "Contact",
         url: "{controller}/{action}/{contactId}",
         defaults: new { controller = "Home", action = "Contact" }
     );

     routes.MapRoute(
         name: "About",
         url: "{controller}/{action}/{aboutId}",
         defaults: new { controller = "Home", action = "About" }
     );

     routes.MapRoute(
         name: "Default",
         url: "{controller}/{action}/{id}",
         defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
     );
  }

然后Contact 网址有效,但About 网址无效。

HomeController 看起来像这样:

  public class HomeController : Controller {
      public ActionResult Index() {
         ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

         return View();
      }

      public ActionResult About(int aboutId) {
         ViewBag.Message = "Your app description page.";

         return View();
      }

      public ActionResult Contact(int contactId) {
         ViewBag.Message = "Your contact page.";

         return View();
      }
   }

这似乎意味着无论控制器动作的名称如何,两个路由不能具有相同数量的参数。如果两个控制器动作有一个同名的参数,那么一切正常。我知道我可以开始做一些非常棘手的事情来解决这个问题,例如将所有参数命名为相同的名称或为操作提供无意义的参数来更改参数的数量,但我实际上想知道幕后发生了什么。

我该如何解决这个问题?

【问题讨论】:

  • 我真的不明白为什么在这种情况下你需要 3 条不同的路线。您需要做的就是删除非默认路由并将aboutIdcontactId 替换为id。一切都应该正常工作......
  • @Aron:这当然是一个人为的例子来演示这个问题。我们有多个具有不同参数的控制器动作。将它们全部命名为相同的东西会严重降低代码的可读性。

标签: c# asp.net-mvc asp.net-mvc-routing


【解决方案1】:

问题的根源(没有双关语)不是路由不能具有相同数量的参数。他们能。问题是路由引擎将选择与传入请求匹配的第一个路由。您的路线仅在默认情况下有所不同,并且在模式匹配方面它们完全相同。因此,在每种情况下,您都应该使用Contact 路线。

您似乎正在尝试根据操作设置不同的路线。我真的不明白你为什么需要。

您可以使用以下内容来实现该效果。

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

    routes.MapRoute(
        name: "Contact",
        url: "Home/Contact/{contactId}"
    );

    routes.MapRoute(
        name: "About",
        url: "Home/About/{aboutId}"
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
 }

但是。我强烈建议不要使用这种方法,因为您的“默认”路由将是 Contact 路由。这意味着(在 Razor 下)@Html.ActionLink() 和相关方法将......错误。

老实说,如果你真的只是使用它应该可以完美地工作......

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

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
 }

【讨论】:

  • 采用第二种解决方案(删除除默认路由之外的所有路由)不起作用并以同样的异常结束。为了使其工作,您需要为所有单参数控制器操作提供相同的名称“id”。我在我的问题中提到了这一点,我认为这不是一个可以接受的解决方案。
  • @AranMulholland - 基于模式匹配的路由工作。除了更改路由以使它们具有不同的模式或重用相同的路由之外,别无选择。您需要某种差异化因素,如果您不愿意这样做,它根本行不通。
  • @MystereMan 但是路由包含参数名称和默认控制器动作,这是否意味着默认控制器动作被忽略?您会认为路由引擎可以确定我打算调用控制器操作作为请求名称控制器和操作。
  • @AranMulholland - 在路由选择过程中不使用名称,仅使用 pattern。您的 about 和 contact pattern 之间没有区别,因此永远不会选择第二个。路由引擎无法知道你指的是哪一个,除非你给它一个微分,变量的名字不是微分。
【解决方案2】:

在路由中明确指定控制器和/或操作以允许路由选择正确的路由:

 routes.MapRoute(
     name: "About",
     url: "{controller}/About/{aboutId}",
     defaults: new { controller = "Home", action = "About" }
 );

您还可以向参数添加约束以区分路由,但在您的情况下,这两个操作看起来都具有相同的整数参数。

【讨论】:

  • 我强烈建议不要使用这种方法,因为它会扰乱您在 @Url.Action 和相关助手上的路由。
  • @Aron - 我不完全确定为什么 Url.Action 不适用于非默认路由...除了您的建议之外,相关文章/问题的链接会有所帮助。
  • 这完全是经验性的。 MVC 不将“默认”识别为某个神奇的名称。因此,“默认”路由是第一个被注册的路由,在本例中是 About 路由。默认情况下,@Url.Action 将使用的路由是 About 路由。因此,例如在渲染@Html.ActionLink("Foo","Bar") 时,您将获得指向http://www.myapp.com/Foo/About/?action=Bar 的链接。这会让你到正确的地方。只是不是很 RESTful。
  • 我也有类似的问题。我在同一个文件夹中有 3 个控制器。系统可以找到默认控制器,但找不到其他控制器,例如/pages/default 工作正常,但 /pages/data 不行
【解决方案3】:

难道您不能删除您的自定义路由并重用 {id} 参数(在您的操作代码中将 {contactId} 和 {aboutId} 变成“id”)吗?

public class HomeController : Controller {
  public ActionResult Index() {
     ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

     return View();
  }

  public ActionResult About(int id) {

     int aboutId = id;

     ViewBag.Message = "Your app description page.";

     return View();
  }

  public ActionResult Contact(int id) {

     int contactId = id;

     ViewBag.Message = "Your contact page.";

     return View();
  }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    • 2014-08-24
    • 2017-03-10
    • 2014-10-07
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    相关资源
    最近更新 更多