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));