ASP.NET MVC模糊的动作方法

Jon*_*and 131 c# asp.net-mvc asp.net-mvc-routing

我有两个冲突的动作方法.基本上,我希望能够使用两个不同的路径来获取相同的视图,可以是项目的ID,也可以是项目的名称及其父项(项目可以在不同的父项中具有相同的名称).搜索项可用于过滤列表.

例如...

Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321
Run Code Online (Sandbox Code Playgroud)

这是我的行动方法(还有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) { ... }
Run Code Online (Sandbox Code Playgroud)

以下是路线......

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

routes.MapRoute("AssignRemovePretty",
                "Items/{action}/{parentName}/{itemName}",
                new { controller = "Items" }
                );
Run Code Online (Sandbox Code Playgroud)

我理解为什么错误发生,因为page参数可以为null,但我无法找出解决它的最佳方法.我的设计开始时很差吗?我已经考虑过扩展Method #1签名以包含搜索参数并将逻辑Method #2移到他们都会调用的私有方法中,但我不相信这会真正解决这种模糊性.

任何帮助将不胜感激.


实际解决方案(基于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; }
}
Run Code Online (Sandbox Code Playgroud)

然后装饰动作方法......

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

[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
Run Code Online (Sandbox Code Playgroud)

Lev*_*evi 179

MVC不支持仅基于签名的方法重载,因此这将失败:

public ActionResult MyMethod(int someInt) { /* ... */ }
public ActionResult MyMethod(string someString) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

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

[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; }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,该属性简单地说"如果请求中存在密钥xxx,则此方法匹配".您还可以根据路径(controllerContext.RequestContext)中包含的信息进行过滤,如果更适合您的目的.

  • 太好了!我还没有看到RequireRequestValue属性.这是一个很好的人. (4认同)

Cod*_*nis 7

在你的路由参数{roleId},{applicationName}并且{roleName}不匹配的动作方法的参数名称.我不知道这是否重要,但它更难以弄清楚你的意图是什么.

你的itemId是否符合可以通过正则表达式匹配的模式?如果是这样,那么您可以为路径添加约束,以便只有与模式匹配的url被标识为包含itemId.

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

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

编辑:您还可以添加成为制约AssignRemovePretty使得两个路径{parentName}{itemName}需要.

编辑2:此外,由于您的第一个操作只是重定向到第二个操作,您可以通过重命名第一个操作来消除一些歧义.

// 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) { ... }
Run Code Online (Sandbox Code Playgroud)

然后在路由中指定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+" }
                );
Run Code Online (Sandbox Code Playgroud)


Ric*_*SFT 7

另一种方法是重命名其中一种方法,这样就不存在冲突.例如

// GET: /Movies/Delete/5
public ActionResult Delete(int id = 0)

// POST: /Movies/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id = 0)
Run Code Online (Sandbox Code Playgroud)

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