Mam dwie sprzeczne metody działania. Zasadniczo chcę mieć możliwość uzyskania tego samego widoku przy użyciu dwóch różnych tras, albo według identyfikatora elementu, albo według nazwy elementu i jego elementu nadrzędnego (elementy mogą mieć tę samą nazwę u różnych elementów nadrzędnych). Do filtrowania listy można użyć wyszukiwanego hasła.
Na przykład...
Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321
Oto moje metody akcji (są też Remove
metody akcji) ...
// 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) { ... }
A oto trasy ...
routes.MapRoute("AssignRemove",
"Items/{action}/{itemId}",
new { controller = "Items" }
);
routes.MapRoute("AssignRemovePretty",
"Items/{action}/{parentName}/{itemName}",
new { controller = "Items" }
);
Rozumiem, dlaczego występuje błąd, ponieważ page
parametr może być pusty, ale nie mogę znaleźć najlepszego sposobu rozwiązania tego problemu. Czy mój projekt jest kiepski? Myślałem o rozszerzeniu Method #1
podpisu tak, aby obejmował parametry wyszukiwania i przeniesieniu logiki Method #2
do prywatnej metody, którą obaj będą wywoływać, ale nie wierzę, że to faktycznie rozwiąże tę niejednoznaczność.
Każda pomoc byłaby bardzo mile widziana.
Rzeczywiste rozwiązanie (na podstawie odpowiedzi Levi's)
Dodałem następującą klasę ...
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; }
}
A potem udekorował metody działania ...
[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }
[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
źródło
return ValueNames.All(v => controllerContext.RequestContext.RouteData.Values.ContainsKey(v));
contains = ...
sekcję na coś takiego:contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value) || controllerContext.RequestContext.HttpContext.Request.Params.AllKeys.Contains(value);
ActionResult DoSomething(Person p)
gdziePerson
ma różne proste właściwości, takie jakName
, a żądania do niego są wysyłane bezpośrednio z nazwami właściwości (np/dosomething/?name=joe+someone&other=properties
.).controllerContext.HttpContext.Request[value] != null
zamiastcontrollerContext.RequestContext.RouteData.Values.ContainsKey(value)
; ale mimo wszystko niezła robota.Odpowiedzi:
MVC nie obsługuje przeciążania metod opartego wyłącznie na sygnaturze, więc to się nie powiedzie:
Jednak to nie metoda wsparcie przeciążenia na podstawie atrybutu:
W powyższym przykładzie atrybut mówi po prostu „ta metoda pasuje, jeśli klucz xxx był obecny w żądaniu”. Możesz również filtrować według informacji zawartych w trasie (controllerContext.RequestContext), jeśli to lepiej pasuje do twoich celów.
źródło
...RouteData.Values
zamiast, ale to „działa”. To, czy jest to dobry wzór, jest otwarte do dyskusji. :)Parametry w swoich tras
{roleId}
,{applicationName}
a{roleName}
nie pasuje do nazwy parametrów w metodach działania. Nie wiem, czy to ma znaczenie, ale utrudnia ustalenie, jakie masz zamiary.Czy element itemId jest zgodny ze wzorcem, który można dopasować za pomocą wyrażenia regularnego? Jeśli tak, możesz dodać ograniczenie do swojej trasy, aby tylko adresy URL pasujące do wzorca były identyfikowane jako zawierające elementId.
Jeśli element itemId zawiera tylko cyfry, to zadziała:
Edycja: możesz również dodać ograniczenie do
AssignRemovePretty
trasy, aby zarówno{parentName}
i{itemName}
były wymagane.Edycja 2: Ponadto, ponieważ Twoja pierwsza akcja to po prostu przekierowanie do drugiej akcji, możesz usunąć pewną niejednoznaczność, zmieniając nazwę pierwszej.
Następnie określ nazwy akcji w swoich trasach, aby wymusić wywołanie właściwej metody:
źródło
Innym podejściem jest zmiana nazwy jednej z metod, aby nie było konfliktu. Na przykład
Zobacz http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs
źródło
Niedawno skorzystałem z okazji, aby ulepszyć odpowiedź @ Levi, aby wspierać szerszy zakres scenariuszy, z którymi miałem do czynienia, takich jak: obsługa wielu parametrów, dopasowanie dowolnego z nich (zamiast wszystkich), a nawet dopasowanie żadnego z nich.
Oto atrybut, którego teraz używam:
Więcej informacji i przykłady implementacji znajdziesz w tym wpisie na blogu, który napisałem na ten temat.
źródło
rozważ użycie biblioteki tras testowych MVC Contribs do przetestowania tras
źródło