J'ai deux méthodes d'action qui sont contradictoires. Fondamentalement, je veux pouvoir accéder à la même vue en utilisant deux itinéraires différents, soit par l'ID d'un élément, soit par le nom de l'élément et celui de son parent (les éléments peuvent avoir le même nom dans différents parents). Un terme de recherche peut être utilisé pour filtrer la liste.
Par exemple...
Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321
Voici mes méthodes d'action (il y a aussi Remove
des méthodes d'action) ...
// 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) { ... }
Et voici les itinéraires ...
routes.MapRoute("AssignRemove",
"Items/{action}/{itemId}",
new { controller = "Items" }
);
routes.MapRoute("AssignRemovePretty",
"Items/{action}/{parentName}/{itemName}",
new { controller = "Items" }
);
Je comprends pourquoi l'erreur se produit, car le page
paramètre peut être nul, mais je ne peux pas trouver le meilleur moyen de le résoudre. Ma conception est-elle médiocre pour commencer? J'ai pensé à étendre Method #1
la signature de pour inclure les paramètres de recherche et à déplacer la logique Method #2
vers une méthode privée qu'ils appelleraient tous les deux, mais je ne pense pas que cela résoudra réellement l'ambiguïté.
Toute aide serait grandement appréciée.
Solution réelle (basée sur la réponse de Levi)
J'ai ajouté la classe suivante ...
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; }
}
Et puis décoré les méthodes d'action ...
[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }
[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
la source
return ValueNames.All(v => controllerContext.RequestContext.RouteData.Values.ContainsKey(v));
contains = ...
section par quelque chose comme ceci:contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value) || controllerContext.RequestContext.HttpContext.Request.Params.AllKeys.Contains(value);
ActionResult DoSomething(Person p)
wherePerson
a diverses propriétés simples commeName
, et les requêtes qui lui sont adressées sont faites directement avec les noms de propriétés (par exemple,/dosomething/?name=joe+someone&other=properties
).controllerContext.HttpContext.Request[value] != null
place decontrollerContext.RequestContext.RouteData.Values.ContainsKey(value)
; mais un beau travail néanmoins.Réponses:
MVC ne prend pas en charge la surcharge de méthode basée uniquement sur la signature, donc cela échouera:
Cependant, il prend en charge la surcharge de méthode basée sur l'attribut:
Dans l'exemple ci-dessus, l'attribut dit simplement "cette méthode correspond si la clé xxx était présente dans la demande". Vous pouvez également filtrer en fonction des informations contenues dans l'itinéraire (controllerContext.RequestContext) si cela vous convient le mieux.
la source
...RouteData.Values
place, mais cela "fonctionne". Que ce soit un bon modèle ou non est sujet à débat. :)Les paramètres de vos itinéraires
{roleId}
,{applicationName}
et{roleName}
ne correspondent pas aux noms de paramètre dans vos méthodes d'action. Je ne sais pas si cela compte, mais il est plus difficile de déterminer quelle est votre intention.Votre itemId est-il conforme à un modèle qui pourrait être mis en correspondance via regex? Si tel est le cas, vous pouvez ajouter une restriction à votre itinéraire afin que seules les URL qui correspondent au modèle soient identifiées comme contenant un itemId.
Si votre itemId ne contenait que des chiffres, cela fonctionnerait:
Modifier: vous pouvez également ajouter une contrainte à l'
AssignRemovePretty
itinéraire afin que les deux{parentName}
et{itemName}
soient obligatoires.Edit 2: De plus, puisque votre première action est simplement une redirection vers votre 2ème action, vous pouvez supprimer une certaine ambiguïté en renommant la première.
Spécifiez ensuite les noms d'action dans vos itinéraires pour forcer la méthode appropriée à être appelée:
la source
Une autre approche consiste à renommer l'une des méthodes afin qu'il n'y ait pas de conflit. Par exemple
Voir http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs
la source
Récemment, j'ai profité de l'occasion pour améliorer la réponse de @ Levi pour prendre en charge un plus large éventail de scénarios que je devais gérer, tels que: prise en charge de plusieurs paramètres, faire correspondre l'un d'entre eux (au lieu de tous) et même ne correspondre à aucun d'entre eux.
Voici l'attribut que j'utilise maintenant:
Pour plus d'informations et des exemples d'implémentation, consultez ce billet de blog que j'ai écrit sur ce sujet.
la source
envisagez d'utiliser la bibliothèque de routes de test MVC Contribs pour tester vos routes
la source