środa, 13 stycznia 2010

Wzorzec “POST Redirect GET” w ASP.NET MVC

Jednym z największych problemów w aplikacjach internetowych jest podwójne przesłanie danych za pomocą metody POST na szczęście prawidłowo korzystając z mechanizmów ASP.NET MVC można się przed tym obronić – ale o tym za chwilę. Na początek krótkie wyjaśnienie tego w czym cały problem.

Wyobraźmy sobie formularz w którym podajemy: Numer karty kredytowej, datę jej ważności, kwotę w eurogąbkach, którą przelewamy na konto oraz przycisk “Wyślij”, czyli standardowy mechanizm płacenia za płatne treści (taaak wiadomo jakie :-) ) w Internecie.  Formularz w oparciu o metodę POST prześle dane do serwera, gdzie zostanie odpalona jakaś skomplikowana logika, którą nie będziemy się teraz przejmować. Efekt jest taki, że na naszym koncie mamy mniej eurogąbek niż mieliśmy jeszcze minutę temu. Wszystko pięknie, ale po drodze coś mogło pójść nie tak i nie otrzymaliśmy informacji zwrotnej, że przelew się udał. Co gorsza strona może prosić o odświeżenie znanym chyba każdemu użytkownikowi, najczęściej niepoprawnie zaimplementowanych stron, komunikatem:

com

Co zrobi przeciętny użytkownik? Tak: kliknie przycisk Prześlij ponownie. I to kosztowny błąd, bo na jego koncie jest dwa razy mniej eurogąbek.

Problem z formularzami wykorzystującymi metodę POST pojawia się gdy:

  1. Odświeżamy stronę.
  2. Zapisujemy stronę do Zakładek przeglądarki internetowej.

Rozwiązanie: PRG

Wzorzec projektowy PRG polega na:

  1. Przesłaniu formularza w sposób standardowy metodą POST.
  2. Przekierowanie, a ściślej mówiąc, zwrócenie kodu 303 / 302.
  3. Zażądanie załadowania “nowej” strony z użyciem GET.

PRG i ASP.NET MVC

Jak to się powinno zrobić w ASP.NET MVC? Służy do tego: znana chyba każdemu – a niestarty często nie do końca przez wszystkich zrozumiana metoda - RedirectToAction wraz z TempData przechowujący dane pomiędzy żądaniami.

[HttpPost]
public ActionResult Edit(InputData input)
{
if (ModelState.IsValid)
{
//jakas logika...
TempData["ResultMessage"] = "Transfer eurogabek zakonczony!";
return RedirectToAction("index");
}

return View(input);
}




Tak zaimplementowana logika nie spowoduje ponownego przesłania danych przy odświeżeniu strony i to właśnie tak powinny być zaprojektowane formularze korzystające z POST.

2 komentarze:

dario pisze...

Co nie zmienia faktu, że można zrobić dwa razy <- Back i znów przesłać ten sam formularz.

Przed tym także można się zabezpieczyć, ale to już wymaga zastosowania swego rodzaju wersjonowania. Na przykład na poziomie obiektu zapisywanego do bazy.

Dariusz Tarczyński pisze...

@dario
Można zrobić dwa razy back, ale w przypadku PRG formularz nie zostanie ponownie przesłany. Powiem więcej: można wysłać formularz, zrobić back, po czym zrobić forward i formularz też NIE zostanie przesłany.