Frage

Ich habe zwei Handlungsmethoden, die in Konflikt stehen. Grundsätzlich mag ich die Lage sein, auf die gleiche Ansicht erhalten unter Verwendung von zwei verschiedenen Routen, entweder durch ein Element ID oder durch das Namen des Elements und seine Eltern (Artikel können die gleichen Namen in verschiedenen Eltern haben). Ein Suchbegriff kann verwendet werden, um die Liste zu filtern.

Zum Beispiel ...

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

Hier sind meine Aktionsmethoden (es gibt auch Remove Aktionsmethoden) ...

// 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) { ... }

Und hier sind die Routen ...

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

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

Ich verstehe, warum der Fehler auftritt, da der page Parameter null sein kann, aber ich kann nicht den besten Weg, herauszufinden, es zu lösen. Ist mein Design schlecht zu beginnen? Ich habe darüber nachgedacht, Method #1 Unterschrift zur Verlängerung der Suchparameter und Bewegen der Logik in Method #2 aus zu einer privaten Methode würde sie beide Anruf zu schließen, aber ich glaube nicht, dass die Mehrdeutigkeit tatsächlich lösen.

Jede Hilfe wäre sehr geschätzt.


Die tatsächliche Lösung (basierend auf Levi Antwort)

Ich habe die folgende Klasse ...

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

Und dann die Aktionsmethoden eingerichtet ...

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

[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
War es hilfreich?

Lösung

MVC unterstützt nicht die Methode ausschließlich auf signaturbasierten Überlastung, so dass dieses fehlschlagen:

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

Es ist jedoch hat Unterstützungsverfahren Überlastung basierend auf Attribut:

[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; }
}

In dem obigen Beispiel das Attribut sagt einfach „diese Methode übereinstimmt, wenn der Schlüssel xxx war in der Anfrage.“ Sie können Informationen filtern auch innerhalb der Route (controllerContext.RequestContext), wenn das besser passt Ihre Zwecke enthalten ist.

Andere Tipps

Die Parameter in Ihren Routen {roleId}, {applicationName} und {roleName} nicht die Parameternamen in den Aktionsmethoden entsprechen. Ich weiß nicht, wenn es ankommt, aber es macht es schwieriger, herauszufinden, was Ihre Absicht ist.

Sie entsprechen Ihr itemId die zu einem Muster, das über regex abgestimmt werden könnte? Wenn ja, dann können Sie eine Zurückhaltung zu Ihrer Route hinzufügen, so dass nur URLs, die dem Muster entsprechen, werden identifiziert als eine itemId enthält.

Wenn Ihr itemId nur Ziffern enthalten, dann würde dies funktionieren:

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

Edit: Sie können auch eine Einschränkung der AssignRemovePretty Route hinzufügen, so dass beide {parentName} und {itemName} erforderlich sind,

.

Bearbeiten. 2: Auch da Ihre erste Aktion auf Ihre zweite Aktion nur umleiten, Sie einige Mehrdeutigkeit durch Umbenennen des ersten entfernen könnten

// 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) { ... }

Sie dann die Aktionsnamen in Ihren Routen geben Sie die richtige Methode zu erzwingen aufgerufen werden:

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+" }
                );

Ein weiterer Ansatz ist eine der Methoden zu benennen, so gibt es keinen Konflikt gibt. Zum Beispiel

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

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

Siehe http://www.asp.net/mvc/tutorials/ getting-started-mit-MVC3-Part9-cs

Vor kurzem habe ich die Chance zu verbessern @ Levi Antwort ein breiteres Spektrum von Szenarien zu unterstützen, ich hatte zu behandeln, wie zum Beispiel: mehrere Parameter Unterstützung, entsprechen einer von ihnen (statt sie alle) und auch keiner von ihnen entsprechen.

Hier ist das Attribut ich jetzt bin mit:

/// <summary>
/// Flags an Action Method valid for any incoming request only if all, any or none of the given HTTP parameter(s) are set,
/// enabling the use of multiple Action Methods with the same name (and different signatures) within the same MVC Controller.
/// </summary>
public class RequireParameterAttribute : ActionMethodSelectorAttribute
{
    public RequireParameterAttribute(string parameterName) : this(new[] { parameterName })
    {
    }

    public RequireParameterAttribute(params string[] parameterNames)
    {
        IncludeGET = true;
        IncludePOST = true;
        IncludeCookies = false;
        Mode = MatchMode.All;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        switch (Mode)
        {
            case MatchMode.All:
            default:
                return (
                    (IncludeGET && ParameterNames.All(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    || (IncludePOST && ParameterNames.All(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    || (IncludeCookies && ParameterNames.All(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
            case MatchMode.Any:
                return (
                    (IncludeGET && ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    || (IncludePOST && ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    || (IncludeCookies && ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
            case MatchMode.None:
                return (
                    (!IncludeGET || !ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    && (!IncludePOST || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    && (!IncludeCookies || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
        }
    }

    public string[] ParameterNames { get; private set; }

    /// <summary>
    /// Set it to TRUE to include GET (QueryStirng) parameters, FALSE to exclude them:
    /// default is TRUE.
    /// </summary>
    public bool IncludeGET { get; set; }

    /// <summary>
    /// Set it to TRUE to include POST (Form) parameters, FALSE to exclude them:
    /// default is TRUE.
    /// </summary>
    public bool IncludePOST { get; set; }

    /// <summary>
    /// Set it to TRUE to include parameters from Cookies, FALSE to exclude them:
    /// default is FALSE.
    /// </summary>
    public bool IncludeCookies { get; set; }

    /// <summary>
    /// Use MatchMode.All to invalidate the method unless all the given parameters are set (default).
    /// Use MatchMode.Any to invalidate the method unless any of the given parameters is set.
    /// Use MatchMode.None to invalidate the method unless none of the given parameters is set.
    /// </summary>
    public MatchMode Mode { get; set; }

    public enum MatchMode : int
    {
        All,
        Any,
        None
    }
}

Für weitere Informationen und Anleitungen zur Durchführung Proben überprüfen diese Blog-Post , die ich zu diesem Thema geschrieben.

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

betrachten Bibliothek mit MVC Contribs Teststrecken Ihre Routen testen

"Items/parentName/itemName".Route().ShouldMapTo<Items>(x => x.Assign("parentName", itemName));
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top