Tengo dos métodos de acción que son conflictivos. Básicamente, quiero poder llegar a la misma vista usando dos rutas diferentes, ya sea por la ID de un elemento o por el nombre del elemento y el de su elemento primario (los elementos pueden tener el mismo nombre en diferentes elementos primarios). Se puede usar un término de búsqueda para filtrar la lista.
Por ejemplo...
Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321
Aquí están mis métodos de acción (también hay Remove
métodos de acción) ...
// 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) { ... }
Y aquí están las rutas ...
routes.MapRoute("AssignRemove",
"Items/{action}/{itemId}",
new { controller = "Items" }
);
routes.MapRoute("AssignRemovePretty",
"Items/{action}/{parentName}/{itemName}",
new { controller = "Items" }
);
Entiendo por qué se produce el error, ya que el page
parámetro puede ser nulo, pero no puedo encontrar la mejor manera de resolverlo. ¿Es mi diseño pobre para empezar? He pensado en extender Method #1
la firma para incluir los parámetros de búsqueda y trasladar la lógica Method #2
a un método privado que ambos llamarían, pero no creo que eso realmente resuelva la ambigüedad.
Cualquier ayuda sería muy apreciada.
Solución real (basada en la respuesta de Levi)
Agregué la siguiente clase ...
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; }
}
Y luego decoró los métodos de acción ...
[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }
[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
fuente
return ValueNames.All(v => controllerContext.RequestContext.RouteData.Values.ContainsKey(v));
contains = ...
sección por algo como esto:contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value) || controllerContext.RequestContext.HttpContext.Request.Params.AllKeys.Contains(value);
ActionResult DoSomething(Person p)
dondePerson
tiene varias propiedades simples comoName
, y las solicitudes se realizan con nombres de propiedad directamente (por ejemplo,/dosomething/?name=joe+someone&other=properties
).controllerContext.HttpContext.Request[value] != null
lugar decontrollerContext.RequestContext.RouteData.Values.ContainsKey(value)
; pero un buen trabajo de todos modos.Respuestas:
MVC no admite la sobrecarga de métodos basada únicamente en la firma, por lo que esto fallará:
Sin embargo, se hace método de soporte sobrecarga basado en el atributo:
En el ejemplo anterior, el atributo simplemente dice "este método coincide si la clave xxx estaba presente en la solicitud". También puede filtrar por información contenida en la ruta (controllerContext.RequestContext) si eso se adapta mejor a sus propósitos.
fuente
...RouteData.Values
en su lugar, pero esto "funciona". Si es o no un buen patrón está abierto a debate. :)Los parámetros en sus rutas
{roleId}
,{applicationName}
y{roleName}
no coinciden con los nombres de los parámetros en sus métodos de acción. No sé si eso importa, pero hace que sea más difícil descubrir cuál es tu intención.¿Su itemId se ajusta a un patrón que podría coincidir a través de regex? Si es así, puede agregar una restricción a su ruta para que solo las URL que coincidan con el patrón se identifiquen como que contengan un itemId.
Si su itemId solo contuviera dígitos, entonces esto funcionaría:
Editar: También puede agregar una restricción a la
AssignRemovePretty
ruta para que ambos{parentName}
y{itemName}
sean obligatorios.Edición 2: Además, dado que su primera acción es simplemente redirigir a su segunda acción, puede eliminar cierta ambigüedad cambiando el nombre de la primera.
Luego, especifique los nombres de Acción en sus rutas para forzar que se llame al método apropiado:
fuente
Otro enfoque es renombrar uno de los métodos para que no haya conflicto. Por ejemplo
Ver http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs
fuente
Recientemente aproveché la oportunidad de mejorar la respuesta de @ Levi para admitir una gama más amplia de escenarios con los que tuve que lidiar, como: compatibilidad con múltiples parámetros, igualar cualquiera de ellos (en lugar de todos) e incluso igualar ninguno de ellos.
Aquí está el atributo que estoy usando ahora:
Para más información y cómo-a muestras de implementación echa un vistazo a esta entrada del blog que escribí sobre este tema.
fuente
considere usar la biblioteca de rutas de prueba de MVC Contribs para probar sus rutas
fuente