Jak przekierowujesz do strony przy użyciu czasownika POST?

133

Gdy wywołujesz RedirectToActionw kontrolerze, automatycznie przekierowuje przy użyciu protokołu HTTP GET. Jak wyraźnie powiedzieć mu, aby używał POST protokołu HTTP?

Mam akcję, która akceptuje zarówno żądania GET, jak i POST, i chcę mieć możliwość RedirectToActionkorzystania z POST i wysyłania do niej pewnych wartości.

Lubię to:

this.RedirectToAction(
    "actionname",
    new RouteValueDictionary(new { someValue = 2, anotherValue = "text" })
);

Chcę, aby wartości someValuei anotherValuebyły wysyłane przy użyciu protokołu HTTP POST zamiast GET. Czy ktoś wie, jak to zrobić?

Chris Pietschmann
źródło
Odpowiedź wysłana przez Jasona będzie działać w większości scenariuszy, jedynym problemem, który widzę, jest to, że jest podatna na wypadki. tj. wywołanie metody akcji bezpośrednio pomija wszystkie filtry zastosowane do akcji. Tak więc, w przypadku, gdy do metody akcji zastosowano filtr uwierzytelniający lub licznik, dane te mogą zostać utracone. Bezpośrednie wywołanie metody akcji zadziała, ale powinno być ostrożnie stosowane.
amarnath chatterjee

Odpowiedzi:

105

HTTP nie obsługuje przekierowania do strony używającej POST. Kiedy gdzieś przekierowujesz, nagłówek HTTP „Lokalizacja” informuje przeglądarkę, dokąd się udać, a przeglądarka wysyła żądanie GET dla tej strony. Prawdopodobnie będziesz musiał po prostu napisać kod strony, aby akceptować żądania GET, a także żądania POST.

Eli Courtwright
źródło
4
Ciekawe, dlaczego moja odpowiedź nie została przyjęta, myślę, że moja retoryka jest rozsądna. :) Z drugiej strony, mogę być trochę stronniczy co do tego ...
Jason Bunting,
14
Chociaż ta odpowiedź jest w zasadzie poprawna, nie jest pełna. Zobacz odpowiedź Jasona Buntinga poniżej, aby uzyskać znacznie lepsze obejście.
Adrian Grigore
160

Dla twojego konkretnego przykładu zrobiłbym to po prostu, ponieważ oczywiście nie obchodzi Cię fakt, że przeglądarka i tak otrzyma przekierowanie (z racji zaakceptowania odpowiedzi, którą już zaakceptowałeś):

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index() {
   // obviously these values might come from somewhere non-trivial
   return Index(2, "text");
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(int someValue, string anotherValue) {
   // would probably do something non-trivial here with the param values
   return View();
}

To działa łatwo i tak naprawdę nie dzieje się nic zabawnego - pozwala to zachować fakt, że druga z nich naprawdę akceptuje tylko żądania HTTP POST (z wyjątkiem tego przypadku, który i tak jest pod Twoją kontrolą) i nie musisz tego robić użyj również TempData, co sugeruje link, który zamieściłeś w swojej odpowiedzi.

Chciałbym wiedzieć, co jest w tym „nie tak”, jeśli jest coś. Oczywiście, jeśli naprawdę chcesz wysłać do przeglądarki przekierowanie, to nie zadziała, ale wtedy powinieneś zapytać, dlaczego próbowałbyś to przekonwertować, skoro wydaje mi się to dziwne.

Mam nadzieję, że to pomoże.

Jason Bunting
źródło
7
Kto wie, dlaczego zostałeś odrzucony. To bardzo przydatna metoda.
Peter J
2
Tak też zawsze rozwiązywałem ten problem. Głosowanie za tym nie ma sensu.
Adrian Grigore
39
Głosowałem „za”, chociaż nie zgadzam się na nazywanie ludzi idiotami, kiedy ich nie znasz.
Jim Schubert,
3
Nie jestem zwolennikiem odrzucenia, ale jedyną ostrożnością jest to, że jeśli chcesz wywołać widok o innej nazwie, lub jeśli parametry są ważne, zostaną utracone. Powodem jest to, że adres URL będzie odzwierciedlał akcję + parametry przed przekierowaniem po stronie serwera. Może to prowadzić do zamieszania przez użytkownika, zwłaszcza jeśli odświeżył stronę, a następnie znalazł się na poprzedniej stronie (ponieważ odświeżenie użyło starego adresu URL). Ta technika jest zasadniczo bardzo podobna do metody Server.Transfer asp.net i należy zachować te same środki ostrożności.
AaronLS
15
Nie głosowałem przeciw jako taki, ale widzę powód, by. Ta metoda narusza konwencję kodowania określoną przez wzorzec MVC. Działa tylko w przypadku wywołania tej samej akcji. Jeśli akcja jest inna, nawet na tym samym kontrolerze, wartości routingu są wkręcone i zostanie zwrócony niewłaściwy widok. Krótko mówiąc: nie rób tego.
erlando,
21

Jeśli chcesz przekazać dane między dwiema akcjami podczas przekierowania bez uwzględniania żadnych danych w ciągu zapytania, umieść model w obiekcie TempData.

AKCJA

TempData["datacontainer"] = modelData;

WIDOK

var modelData= TempData["datacontainer"] as ModelDataType; 

TempData ma być instancją krótkotrwałą i należy jej używać tylko podczas bieżącego i kolejnych żądań! Ponieważ TempData działa w ten sposób, musisz wiedzieć na pewno, jakie będzie następne żądanie, a przekierowanie do innego widoku jest jedynym momentem, w którym możesz to zagwarantować.

Dlatego jedynym scenariuszem, w którym korzystanie z TempData będzie niezawodne, jest przekierowanie.

Otto Kanellis
źródło
12

Spróbuj tego

return Content("<form action='actionname' id='frmTest' method='post'><input type='hidden' name='someValue' value='" + someValue + "' /><input type='hidden' name='anotherValue' value='" + anotherValue + "' /></form><script>document.getElementById('frmTest').submit();</script>");
vicky
źródło
4
Nienawidzę tego, ale kocham :)
divinci
Taki hack, ale to był jedyny sposób, w jaki mogłem robić, co chciałem, bez naruszania DRY lub zmiany okablowania całej mojej konfiguracji! Dzięki!
jamheadart
6

Chciałbym rozszerzyć odpowiedź Jasona Buntinga

lubię to

ActionResult action = new SampelController().Index(2, "text");
return action;

Eli przyjedzie po pomysł, jak uczynić to zmienną ogólną

Może uzyskać wszystkie typy kontrolerów

Yitzhak Weinberg
źródło
2
Nie powinieneś tworzyć instancji dla kontrolera z, new ...()ponieważ stracisz RequestContext- jeśli jesteś już w tym samym kontrolerze, może nie być konieczne tworzenie nowej instancji. W przeciwnym razie postępuj w następujący sposób: SampelController sampleController = DependencyResolver.Current.GetService<SampelController>()wtedy: sampleController.ControllerContext = new ControllerContext(Request.RequestContext, sampleController);wtedy możesz return sampleController.Index(2, "text");Tylko podpowiedź :)
Matthias Burger