17 komentářů k článku Post-Redirect-Get (nejen) v ASP.NET MVC:

  1. architekt_

    Post-Redirect-Get je kontroverzní návrhový vzor

    Pěkný článek, jen si myslím, že Post-Redirect-Get je obecně rozšířený omyl, který dává pocit bezpečnosti, ale prakticky nic moc neřeší.

    Hlavním jeho nedostatkem je, že neřeší situaci, kdy uživatel použije v prohlížeči zpět a formulář odešle znovu (což je častější případ než že zmáčkne F5). Takže je to jen polovičaté řešení, které vůbec nemusí řešit to, co si klade za cíl. Proto se taky skoro nepoužívá (tedy ne z důvodu ochrany proti opakovanému odeslání dat, jak je prezentováno v článku).

    Tento problém se řeší až na straně aplikace. A to tak, že každý formulář obsahuje jedinečný klíč (jak je dnes dobrým zvykem i v rámci ochrany proti CSRF), podle kterého aplikace pozná, jestli se jedná o opakovaně odeslaný formulář.

    1. developer_

      Re: Post-Redirect-Get je kontroverzní návrhový vzor

      Používáme Post-Redirect-Get ve všech aplikacích tak asi 10 let a nemáme jediný problém s duplicitou odeslaných dat. Neznám ani framework s podporou jedinečných klíčů. To je pro změnu můj názor za léta programování pro web.

    2. Michal AugustýnAutor příspěvku

      Re: Post-Redirect-Get je kontroverzní návrhový vzor

      Díky za reakci a doplnění. Použití tlačítka Zpět je opravdu jedním z dalších problémů, který PRG řeší (a IMHO ho opravdu řeší).

      Upřímně řečeno mě ani nenapadl problém s tlačítkem zpět (tedy to, že by ho PRG neřešil). Když server vrátí jako reakci na POST jen hlavičku s redirektem (nějakou třístovku), tak prohlížeč tuto POSTovanou adresu do historie neuloží (teď testováno na IE8, Firefox 3 a Chrome 2) – však by ani nevěděl, jaký title jí přiřadit.

      Samozřejmě jistě existují prohlížeče či konfigurace prohlížečů, které nám do historie uloží i POSTované stránky, ale IMHO jsou v drtivé menšině.

      1. Ladislav Thon

        Re: Post-Redirect-Get je kontroverzní návrhový vzor

        IMHO pán architekt měl na mysli situaci, kdy se uživatel po regulérním odeslání formuláře vrátí na předchozí stránku s formulářem a znovu klikne na „Odeslat“. To opravdu redirect after POST nevyřeší.

        Což pravda nic nemění na tom, že redirect after POST je naprosto základní technika, která by se měla aplikovat vždy a všude :-)

      2. architekt_

        Re: Post-Redirect-Get je kontroverzní návrhový vzor

        Jde o to, že po tom použití tlačítka zpět uživatel znovu odešle formulář. Typicky stránka (skript) která formulář zpracuje je v pořádku (všechny data se zpracovaly v pořádku) a až na té přesměrované se něco „pokazí“ (nenačte se správně, internal server error, atp.). Když dá uživatel F5, nic se nestane, protože to řeší PRG. Ale spousta (většina) uživatelů F5 nezná/nepoužívá, dají místo toho zpět a pošlou formulář znovu.

        Mimochodem s tím jedinečným klíčem ve formuláři se dá pořešit i situace, kdy dojde k neočekávané chybě už během zpracovávání dat z formuláře. Né vždy může celé zpracování probíhat jako transakce, která když se nedokončí, tak se nic nestane. Po opětovném odeslání formuláře uživatelem může aplikace ověřit/dokončit předchozí pokus o zpracování dat.

    3. Michal AugustýnAutor příspěvku

      Re: Post-Redirect-Get je kontroverzní návrhový vzor

      Aha, přečetl jsem si Vaši reakci ještě jednou a už asi chápu, co jste myslel – návrat na původní GETnutou stránku (často vytaženou z cache) a její další odeslání. Pak je jistě dobrou ochranou použití CSRF tokenu. V ASP.NET MVC to lze velmi jednoduše – při renderování View stačí zavolat Html.AntiForge­ryToken() a POSTovací akci odekorovat atributem ValidateAntiFor­geryToken.

    4. xurfa

      Re: Post-Redirect-Get je kontroverzní návrhový vzor

      Jasně, ale je tu jeden zásadní rozdíl: při Post-Redirect-Get je jediná možnost, jak může uživatel formulář odeslat ta, že vyvolá explicitní odeslání formuláře (tj. např. siskne tlačítko ve formuláří, nebo odentruje pole ve formuláři). Nikdy se to nestane mimoděk (např. tlačítkem zpět, apod.).

  2. Honza77

    Opakované odeslání klávesou F5 to neřeší

    Opakované odeslání klávesou F5 to neřeší, pokud F5 stiskneme ještě v době, kde se zpracovává první stránka (tedy ta, která zpracovává přijmutá data a následně odesílá redirect).

    Ale jinak samozřejmě je základní technika, i když ji nelze použít pro zabránění odeslání formuláře vícekrát.

    1. Michal AugustýnAutor příspěvku

      Re: Opakované odeslání klávesou F5 to neřeší

      Dle mého názoru řeší (teď otestováno na IE8, Firefox 3 a Chrome 2). Browsery totiž po stisku F5, CTRL + F5 i refreshovacího tlačítka začnou znovu načítat poslední stránku z historie, kamžto se POSTovací request vůbec nedostane.

  3. 51><

    informace uzivateli o uspesnosti / neuspesnosti

    Zdravim,

    PRG je nahodou celkem pekne reseni (asi otazka nazoru). Rad bych ale nadhodil k diskuzi, jak resit vetsnou uzivatelu ocekavanou zpravu o uspesnosti / neuspesnosti pozadavku.

    situace rekneme pridani uzivatele v nejakem foru: GET /user/add
    – uzivatel vyplni formular POST /user/add
    – server zpracuje pozadavek a uzivatele do DB prida, posle 303 na /user/list GET /user/list
    – uzivatel vidi seznam jiz existujicich uzivatelu

    ovsem u tohoto seznamu urcite nechce hledat sveho uzivatele, zda byl pridan nebo ne. Tedy rad by videl krasnou zdlenou hlasku „uzivatel uspesne pridan“

    jedno reseni, ktere me napada je poslat si zpravu jako get parametr, napriklad /user/list?msg=us­pesne%20pridan

    toto reseni ma ale tu nevyhodu, ze ted kdyz uzivatel provede reload, uvidi zpravu znovu a zdesi se, ze uzivatel byl pridan znovu. Jine reseni me napada pridat si treba hlavicku do HTTP, kterou pak parsuji napr. javascriptem a na zaklade ni uzivateli zobrazim zpravu. Ale to mi zase neprijde moc koser vuci HTTP

    jak toto resite vy?

    1. Michal AugustýnAutor příspěvku

      Re: informace uzivateli o uspesnosti / neuspesnosti
      V ASP.NET MVC to řeším přes TempData. Pokud nepracujete v ASP.NET MVC, funguje to takto:

      • 1) Kdykoliv během zpracování požadavku na POST /user/add se uloží do session informace o úspěšnosti přidání.
      • 2) Provede se přesměrování na GET /user/list
      • 3) Při zpracování požadavku na GET /user/list se kouknu do session, jestli tam není nějaká zpráva k zobrazení.
      • 4) Na konci zpracování požadavku vymažu data se session.

      Možná to vypadá složitě, ale princip fungování TempData je prostý:

      • 1) Na začátku požadavku se načtou do kolekce TempData všechny položky ze session (resp. načtou se z nějaké položky session, která je kolekcí).
      • 2) Na konci požadavku se do session uloží jen ty hodnoty z TempData, které byly přidány nebo upraveny.

      Předání dat při přesměrování pak vypadá tak, že před přesměrováním data do TempData uložím a ve zpracování požadavku na GET /user/list se prostě kouknu do TempData, jestli tam něco není.
      Btw. v ASP.NET MVC může být TempData implementováno více způsoby – defaultně je to pomocí session, ale existuje i varianta s cookies.

      1. 51><

        Re: informace uzivateli o uspesnosti / neuspesnosti

        Vida, do session si ukladat veci, o kterych pri nejblizsi prilezitosti informovat … proc me to nenapadlo driv :)

        Dobry napad, dik

      2. Viktor

        Re: informace uzivateli o uspesnosti / neuspesnosti

        A jak řešíte situace kdy není jednoznačné kam chce dále uživatel pokračovat, například nechce zobrazit seznam, ale přímo záznam vytvořeného uživatele, nebo chce ten seznam, nebo dokonce zůstat na stránce a přidat dalšího uživatele, atd…? Na straně serveru při zpracování post vyberete ten správný redirect, pokud ano, dává Vám to smysl? ;)

        1. Michal AugustýnAutor příspěvku

          Re: informace uzivateli o uspesnosti / neuspesnosti

          No tak to už samozřejmě záleží jen na návrhu GUI, co přesně se má stát po úspěšném přidání uživatele ;-)

          Jinak v jednom projektu to mám udělané tak, že do TempData ukládám záznam s klíčem „message“ a pokud je v TempData již uložená, kopíruji ji do ViewData. Master page šablona pak vypadá tak, že je připravena na zobrazení zprávy z ViewData (pokud je tam něco uložené). Tak mám jednoduše udělané, že každá stránka je schopna zobrazit jakoukoliv zprávu.

  4. Ruthion

    Re: Post-Redirect-Get (nejen) v ASP.NET MVC

    Redirekty a různé sofistikované ochrany před XSRF jsou pěkná věc, ale jen do chvíle, než opravdu potřebujete nějakou akci udělat 10× a místo 10-ti zmáčknutí F5 musíte 10× vyplňovat tatáž data. Proto mám raději po vyplnění formuláře stránku, na které je jen a pouze výsledek operace a bez redirektu (automatického).

    1. Michal AugustýnAutor příspěvku

      Re: Post-Redirect-Get (nejen) v ASP.NET MVC

      Abych pravdu řekl, nikdy jsem tohle (jako uživatel) nepotřeboval.
      Můj názor je takový, že když jste to někde potřeboval tak jsou (minimálně) dvě možnosti:
      1) Dělal to tam každý uživatel a web byl tudíž blbě navržen, protože měl s tímto scénařem počítat.
      2) Dělal jste to tam „jen vy“, tedy byl to raritní případ, a pro ty IMHO nemá smysl optimalizovat.

Napsat komentář

Tato diskuse je již příliš stará, pravděpodobně již vám nikdo neodpoví. Pokud se chcete na něco zeptat, použijte diskusní server Devel.cz

Zdroj: https://www.zdrojak.cz/?p=3064