ASP.NET MVC含糊不清的操作方法

我有两个冲突的行动方法。 基本上,我希望能够使用两个不同的路线,通过一个项目的ID或项目的名称和它的父母(项目可以有不同的父母相同的名称)的相同的看法。 search项可以用来过滤列表。

例如…

Items/{action}/ParentName/ItemName Items/{action}/1234-4321-1234-4321 

这里是我的操作方法(也有Remove操作方法)…

 // Method #1 public ActionResult Assign(string parentName, string itemName) { // Logic to retrieve item's ID here... string itemId = ...; return RedirectToAction("Assign", "Items", new { itemId }); } // Method #2 public ActionResult Assign(string itemId, string searchTerm, int? page) { ... } 

这里是路线…

 routes.MapRoute("AssignRemove", "Items/{action}/{itemId}", new { controller = "Items" } ); routes.MapRoute("AssignRemovePretty", "Items/{action}/{parentName}/{itemName}", new { controller = "Items" } ); 

我明白为什么错误发生,因为page参数可以为空,但我不能找出解决它的最佳方法。 我的devise很糟糕吗? 我想过扩展Method #1的签名以包含search参数并将Method #2的逻辑移出到他们都会调用的私有方法,但是我不相信这将实际上解决含糊不清的问题。

任何帮助将不胜感激。


实际的解决scheme (基于Levi的答案)

我添加了以下类…

 public class RequireRouteValuesAttribute : ActionMethodSelectorAttribute { public RequireRouteValuesAttribute(string[] valueNames) { ValueNames = valueNames; } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { bool contains = false; foreach (var value in ValueNames) { contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value); if (!contains) break; } return contains; } public string[] ValueNames { get; private set; } } 

然后装饰的行动方法…

 [RequireRouteValues(new[] { "parentName", "itemName" })] public ActionResult Assign(string parentName, string itemName) { ... } [RequireRouteValues(new[] { "itemId" })] public ActionResult Assign(string itemId) { ... } 

MVC不支持完全基于签名的方法重载,所以这将失败:

 public ActionResult MyMethod(int someInt) { /* ... */ } public ActionResult MyMethod(string someString) { /* ... */ } 

但是,它支持基于属性的方法重载:

 [RequireRequestValue("someInt")] public ActionResult MyMethod(int someInt) { /* ... */ } [RequireRequestValue("someString")] public ActionResult MyMethod(string someString) { /* ... */ } public class RequireRequestValueAttribute : ActionMethodSelectorAttribute { public RequireRequestValueAttribute(string valueName) { ValueName = valueName; } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { return (controllerContext.HttpContext.Request[ValueName] != null); } public string ValueName { get; private set; } } 

在上面的例子中,属性简单地说:“如果请求中存在密钥xxx,则此方法匹配”。 如果更适合您的目的,也可以通过路由(controllerContext.RequestContext)中包含的信息进行过滤。

您的路线{roleId}{applicationName}{roleName}中的参数与您的操作方法中的参数名称不匹配。 我不知道这个问题是否重要,但是要弄清楚你的意图是很难的。

你的itemId是否符合可以通过正则expression式匹配的模式? 如果是这样,那么你可以添加约束到你的路线,以便只有符合模式的url被识别为包含itemId。

如果你的itemId只包含数字,那么这将工作:

 routes.MapRoute("AssignRemove", "Items/{action}/{itemId}", new { controller = "Items" }, new { itemId = "\d+" } ); 

编辑:你也可以添加一个约束到AssignRemovePretty路由,这样{parentName}{itemName}都是必需的。

编辑2:另外,由于你的第一个动作只是redirect到你的第二个动作,你可以通过重命名第一个动作来消除一些不明确的地方。

 // Method #1 public ActionResult AssignRemovePretty(string parentName, string itemName) { // Logic to retrieve item's ID here... string itemId = ...; return RedirectToAction("Assign", itemId); } // Method #2 public ActionResult Assign(string itemId, string searchTerm, int? page) { ... } 

然后在路由中指定Action名称以强制调用正确的方法:

 routes.MapRoute("AssignRemove", "Items/Assign/{itemId}", new { controller = "Items", action = "Assign" }, new { itemId = "\d+" } ); routes.MapRoute("AssignRemovePretty", "Items/Assign/{parentName}/{itemName}", new { controller = "Items", action = "AssignRemovePretty" }, new { parentName = "\w+", itemName = "\w+" } ); 

另一种方法是重命名其中一种方法,以免发生冲突。 例如

 // GET: /Movies/Delete/5 public ActionResult Delete(int id = 0) // POST: /Movies/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id = 0) 

http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs

 routes.MapRoute("AssignRemove", "Items/{parentName}/{itemName}", new { controller = "Items", action = "Assign" } ); 

考虑使用MVC Contribstesting路线库来testing你的路线

 "Items/parentName/itemName".Route().ShouldMapTo<Items>(x => x.Assign("parentName", itemName));