Eu tenho dois métodos de ação que são conflitantes. Basicamente, quero conseguir a mesma exibição usando duas rotas diferentes, pelo ID de um item ou pelo nome do item e do pai (os itens podem ter o mesmo nome entre pais diferentes). Um termo de pesquisa pode ser usado para filtrar a lista.
Por exemplo...
Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321
Aqui estão meus métodos de ação (também existem Remove
métodos de ação) ...
// 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) { ... }
E aqui estão as rotas ...
routes.MapRoute("AssignRemove",
"Items/{action}/{itemId}",
new { controller = "Items" }
);
routes.MapRoute("AssignRemovePretty",
"Items/{action}/{parentName}/{itemName}",
new { controller = "Items" }
);
Entendo por que o erro está ocorrendo, pois o page
parâmetro pode ser nulo, mas não consigo descobrir a melhor maneira de resolvê-lo. Meu projeto é ruim para começar? Pensei em estender Method #1
a assinatura para incluir os parâmetros de pesquisa e mover a lógica Method #2
para um método privado que eles chamariam, mas não acredito que isso realmente resolva a ambiguidade.
Qualquer ajuda seria muito apreciada.
Solução real (com base na resposta de Levi)
Eu adicionei a seguinte classe ...
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; }
}
E então decorou os métodos de ação ...
[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }
[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
fonte
return ValueNames.All(v => controllerContext.RequestContext.RouteData.Values.ContainsKey(v));
contains = ...
seção por algo assim:contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value) || controllerContext.RequestContext.HttpContext.Request.Params.AllKeys.Contains(value);
ActionResult DoSomething(Person p)
ondePerson
possui várias propriedades simples comoName
, e solicitações são feitas diretamente com os nomes das propriedades (por exemplo,/dosomething/?name=joe+someone&other=properties
).controllerContext.HttpContext.Request[value] != null
vez decontrollerContext.RequestContext.RouteData.Values.ContainsKey(value)
; mas um bom trabalho, no entanto.Respostas:
O MVC não suporta sobrecarga de método baseada apenas em assinatura, portanto, isso irá falhar:
No entanto, ele faz método de apoio sobrecarga com base no atributo:
No exemplo acima, o atributo simplesmente diz "este método corresponde se a chave xxx estava presente na solicitação". Você também pode filtrar pelas informações contidas na rota (controllerContext.RequestContext), se isso for mais adequado aos seus objetivos.
fonte
...RouteData.Values
lugar, mas isso "funciona". Se é ou não um bom padrão, está aberto ao debate. :)Os parâmetros em suas rotas
{roleId}
,{applicationName}
e{roleName}
não correspondem aos nomes de parâmetro em seus métodos de ação. Não sei se isso importa, mas torna mais difícil descobrir qual é a sua intenção.O itemId está em conformidade com um padrão que pode ser correspondido via regex? Nesse caso, você pode adicionar uma restrição ao seu percurso para que apenas os URLs que correspondam ao padrão sejam identificados como contendo um itemId.
Se seu itemId contivesse apenas dígitos, isso funcionaria:
Editar: Você também pode adicionar uma restrição à
AssignRemovePretty
rota para que ambos{parentName}
e{itemName}
sejam necessários.Editar 2: além disso, como sua primeira ação está apenas redirecionando para a segunda, você pode remover alguma ambiguidade renomeando a primeira.
Em seguida, especifique os nomes de Ação em suas rotas para forçar o método apropriado a ser chamado:
fonte
Outra abordagem é renomear um dos métodos para que não haja conflito. Por exemplo
Consulte http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs
fonte
Recentemente, aproveitei a oportunidade para melhorar a resposta do @ Levi para dar suporte a uma ampla variedade de cenários com os quais tive que lidar, como: suporte a vários parâmetros, igualar qualquer um deles (em vez de todos) e até mesmo nenhum deles.
Aqui está o atributo que estou usando agora:
Para obter mais informações e exemplos de implementação de instruções, consulte esta postagem de blog que escrevi sobre este tópico.
fonte
considere usar a biblioteca de rotas de teste do MVC Contribs para testar suas rotas
fonte