vícenásobného odeslání formuláře
- romansklenar
- Člen | 655
- Ondřej Mirtes
- Člen | 1536
Stručně: Odeslání formuláře mění stav serveru, po jeho úspěšném
odeslání má následovat redirect na tvar URL, která již formulář
neodesílá – např. $this->redirect('this')
.
- phx
- Člen | 651
TeeBee87 napsal(a):
narazil jsem na problém vícenásobného odeslání formuláře.
TeeBee87 myslel naprikald situaci kdy server reaguje pomalu a uzivatel netrpelive klikne znovu. Tz ze prohlizec posle 2. pozadavek a uz mame data na serveru 2×.
Jedine co me napada, ze aby kazdy formular mel v hidden policku nejaky unikatni klic overovany vuci session. Prvni pozadavek klic ze session zdusi a druhe kliknuti jiz data neulozi. Bohuzel uzivatel by asi dostal hlasku o nejakem zabezpeceni a znovu by formular odeslal a tetokrat by to proslo. (novy unikatni identifikator formulare). Tak neivm…
- mancze
- Člen | 58
Myslím, že tohle je skutečně věcná připomínka. Násobné odesílání formulářů se děje a to poměrně často (divili byste se, kolik lidí odesílá formulář dvojklikem).
phx napsal(a):
Jedine co me napada, ze aby kazdy formular mel v hidden policku nejaky unikatni klic overovany vuci session. Prvni pozadavek klic ze session zdusi a druhe kliknuti jiz data neulozi. Bohuzel uzivatel by asi dostal hlasku o nejakem zabezpeceni a znovu by formular odeslal a tetokrat by to proslo. (novy unikatni identifikator formulare). Tak neivm…
Tohle se mi zdá jako pěkné řešení – každý formulář má svůj token, takže je možné jen jedno odeslání na token.
EDIT: No a přidat nějaký přepínač, aby pro vývoj by bylo možné formuláře odesílat násobně (kvůli laďění).
Editoval mancze (1. 4. 2009 19:21)
- phx
- Člen | 651
Ha uz jsem to domyslel. Puvodne mjsem videl problem v tom, ze prvni odeslani ulozi data a druhe vypise chybu. Uzivatel vidi chybu odesle znovu (znovu ulozeno).
Proto je nutne ne zaznam ze session smazat, ale oznacit jako jiz provedeny a misto chyby normalne presmerovat jako ze vse OK.
Ale nemyslim si, ze by to melo resit neco v AppForm nebo v Nette. Maximalne napsat nejaky sikovny formularovy prvek, ktery se do formulare vlozi a pred samotnym ukladamin se onoho prvku zeptam zda mohu ulozit:) Ujme se toho nekdo? Ja osobne to zatim nepotrebuji tak se k tomu moc brzi nedostanu.
- TeeBee87
- Člen | 14
phx napsal(a):
Ha uz jsem to domyslel. Puvodne mjsem videl problem v tom, ze prvni odeslani ulozi data a druhe vypise chybu. Uzivatel vidi chybu odesle znovu (znovu ulozeno).
Proto je nutne ne zaznam ze session smazat, ale oznacit jako jiz provedeny a misto chyby normalne presmerovat jako ze vse OK.
Ale nemyslim si, ze by to melo resit neco v AppForm nebo v Nette. Maximalne napsat nejaky sikovny formularovy prvek, ktery se do formulare vlozi a pred samotnym ukladamin se onoho prvku zeptam zda mohu ulozit:) Ujme se toho nekdo? Ja osobne to zatim nepotrebuji tak se k tomu moc brzi nedostanu.
To se mi líbí – momentálně si s tím hraju, ale nedaří se mi nasimulovat microclick nebo jak to nazvat :)
- Honza Marek
- Člen | 1664
Proti dvojkliku by možná mohlo pomoct v jQuery něco takového:
$("form").submit(function () {
$(this).find(":submit").attr("disabled", "disabled");
});
Nezkoušel jsem to.
- David Grudl
- Nette Core | 8218
Někdy se internet courá a člověk to řeší odesláním formuláře znovu. To je normální a kolikrát užitečné. Zablokování odesílacího tlačítka mu to účinně znemožníme, ale to není dobře – jak má potom odeslat formulář podruhé, když se očividně „něco kouslo“. Možná by nebylo špatné tlačítko zablokovat s časovačem na 5–10 sekund a poté je odblokovat.
$("form").submit(function() {
var el = $(this).find(":submit, :image");
el.attr("disabled", "disabled");
setTimeout(function() {
el.attr("disabled", "");
}, 5000);
});
U pomalu odesílaného formuláře hrozí ještě jedna věc: uživatel si
všimne, že něco špatně vyplnil, položku opraví a dá odeslat znovu. Tomu
by se dalo zabránit úpravou
na var el = $(this).find(":input");
Dále je tu případ, kdy formulář znovuodešle pomocí tlačítka Obnovit stránku. Tohle už je potřeba řešit na straně serveru. Třeba pohledem do databáze, jestli tam už stejný záznam není, nebo obecněji, kdy obsah formuláře (nebo jeho hash) uložíme do session s rozumným timeoutem a s tou porovnáváme. Zároveň je potřeba připojit informaci o tom, kam po odeslání přesměrovat.
if ($form->isSubmitted() && $form->isValid()) {
$values = $form->getValues();
$session = Environment::getSession('App/Forms/CommentForm/Check');
if ($session->values == $values) { // dvojite odeslani
$id = $session->id;
} else {
$id = $model->process($values); // predpokladejme, ze v pripade chyby vyhodi vyjimku
$session->values = $values;
$session->id = $id;
$session->setExpiration(60);
}
$this->redirect('view', $id);
}
- mancze
- Člen | 58
David Grudl napsal(a):
Dále je tu případ, kdy formulář znovuodešle pomocí tlačítka Obnovit stránku. Tohle už je potřeba řešit na straně serveru. Třeba pohledem do databáze, jestli tam už stejný záznam není, nebo obecněji, kdy obsah formuláře (nebo jeho hash) uložíme do session s rozumným timeoutem a s tou porovnáváme. Zároveň je potřeba připojit informaci o tom, kam po odeslání přesměrovat.
Nějak se mi zdá, že tlačítko Obnovit stránku je ekvivalentní s double-postem. Tedy přesněji řešeno – obnovit stránku považuji za hodně nepravděpodobný příklad. Přece pokud bych obnovil stránku se zaseknutým formulářem, tak docílím jen toho, že se mi refreshne stránka s formulářem (a jisté prohlížeče při té příležitosti i resetnou políčka).
Abych znovu-odeslal formulář pomocí Obnovit stránku, tak by muselo být splněno:
- nedodržuju best practice a po správně vyplněném formuláři neredirectuju (nebo)
- browser již dostal odpověď od serveru (přehodí se aktuální stránka na tu s post daty), ale je natolik pomalý, že než zpracuje hlavičku „Location“, tak uživatel stihne klepnout na F5 (nebo)
- je vypnutý redirect
Ani jedno se mi nezdá příliš pravděpodobně (hlavně porovnám-li to četnosti „double-postů“.
phx napsal(a):
Ale nemyslim si, ze by to melo resit neco v AppForm nebo v Nette.
A proč ne? Myslím si, že by toto Nette mělo řešit – hodí se to přece u prakticky každého formuláře, tak proč to všude explicitně ošetřovat? Jdeme přece směrem convention over exception.
Nemyslím si tedy, že by Nette mělo řešit ty opravy ve formulářích, jak naznačil David, ale čistě problém samotných násobných odeslání.
Editoval mancze (2. 4. 2009 17:24)
- David Grudl
- Nette Core | 8218
mancze napsal(a):
- browser již dostal odpověď od serveru (přehodí se aktuální stránka na tu s post daty), ale je natolik pomalý, že než zpracuje hlavičku „Location“, tak uživatel stihne klepnout na F5 (nebo)
- je vypnutý redirect
Ani jedno se mi nezdá příliš pravděpodobně (hlavně porovnám-li to četnosti „double-postů“.
Pak máš štěstí na kvalitní internet a servery s bleskovou odezvou, mně se to stává naprosto běžně.
- mancze
- Člen | 58
David Grudl napsal(a):
Pak máš štěstí na kvalitní internet a servery s bleskovou odezvou, mně se to stává naprosto běžně.
Chceš říct, že když klepneš na tlačítko pro odeslání formuláře a posléze na „Obnovit stránku“, že se formulář odešle podruhé? (jasně, je to hloupst, ale zajímalo by mne to)
- David Grudl
- Nette Core | 8218
mancze napsal(a):
David Grudl napsal(a):
Pak máš štěstí na kvalitní internet a servery s bleskovou odezvou, mně se to stává naprosto běžně.
Chceš říct, že když klepneš na tlačítko pro odeslání formuláře a posléze na „Obnovit stránku“, že se formulář odešle podruhé? (jasně, je to hloupst, ale zajímalo by mne to)
Ne hned, samozřejmě, ale když je to ve fázi, že se prohlížeč snaží načítat už další stránku.
- phx
- Člen | 651
mancze napsal(a):
phx napsal(a):
Ale nemyslim si, ze by to melo resit neco v AppForm nebo v Nette.
A proč ne? Myslím si, že by toto Nette mělo řešit – hodí se to přece u prakticky každého formuláře, tak proč to všude explicitně ošetřovat? Jdeme přece směrem convention over exception.
Nemyslím si tedy, že by Nette mělo řešit ty opravy ve formulářích, jak naznačil David, ale čistě problém samotných násobných odeslání.
Ja jsem to myslel tak, ze si nedovedu predstavit jak by to Nette resilo. Tak jako tak musim JA rozhodnout co udelat kdyz mi dorazi data 2×. Osobne bych to resil pres vlastni FormControl, ktereho bych se ptal zda muzu s klidem ulozit. Mozna by to slo zakomponovat do AppForm (Form), ale nejak si to nedovedu moc predstavit.
Jinak s disable tlacitka souhlasim, ale jak psal David tak max na x sekund.
A posledni vec. Prijde mi zbytecne provnavat data s DB zda tam jiz nahodou nejsou. Bohate by mel stacit onen unikatni identifikator, ktery vuci session budu overovat zda jib bylo/nebylo ulozeno. Kzdy formular odesle znovu data tak odesle i ten samy identifikator. A pokud po ulozeni presmeruji tak by to melo byt OK. Nebo se pletu?
- nAS
- Člen | 277
Co takhle k formulářům přidat něco ve stylu:
$form->onDuplicateSubmit[] = array($this, 'FormDuplicateSubmitted');
a v tom případě by se ve formuláři nastavilo nějaké hidden políčko s vygenerovaným tokenem. A při odeslání formuláře by se ten token uložil do SESSION, takže by se dalo zjistit, zda byl formulář odeslán poprvé, nebo znova. Má to tu výhodu, že si může člověk sám určit, jestli u tohoto konkrétního formuláře chce řešit vícenásobné odeslání nebo ne, a může si sám určit, jak s ním naložit.
- Roman Ožana
- Člen | 52
Tohle není špatné, mě se líbí:
<?php
$form->onMultiSubmit[] = array($this, 'FormMultiSubmitted');
?>
včetně toho hidden parametru
- Honza Kuchař
- Člen | 1662
Jsem pro extra form control na který si budu moct navěsit vlastní fci pomcí callbacku.
- Ondřej Mirtes
- Člen | 1536
honzakuchar napsal(a):
Teď jsem to zkoušel a jde odeslat několikrát. (odešlu form a potom dám F5)
Takhle to nezjistíš. Po tom F5 už má formulář nový token, ne?
- Honza Kuchař
- Člen | 1662
Po tom F5 už má formulář nový token, ne?
Nemá, token je platný po nastavenou dobu. (zkoušel jsem se přihlásit, odhlásit a já nevím co, ale token ve formuláři byl pořád stejný)
- Ondřej Mirtes
- Člen | 1536
David Grudl napsal(a):
Někdy se internet courá a člověk to řeší odesláním formuláře znovu…
Díky za skript, ale nedaří se mi ho upravit, aby se tlačítko nedisablovalo, pokud na něj uživatel sice klikne, ale před odesláním ještě zasáhne Nette validace s nějakým tím alertem.
Může mě někdo pošťouchnout správným směrem? Díky.
- Ondřej Mirtes
- Člen | 1536
Tak jsem se po X měsících hrabal v tom javascriptovém disablování submit tlačítka a musel jsem sáhnout do zdrojáků Nette. Nedařilo se mi v mém kódu dostat se k výsledku už proběhlé „onsubmit“ události, kde by mohl být uložen výsledek validace – tlačítko bych pak disabloval jen v případě, že validace proběhla správně.
Upravil jsem si tedy Nette, obohatil jsem funkci validateForm o druhý parametr, který říká, jestli má vyskočit alert okno se zprávou pro uživatele – a při submitování si tuto metodu zkrátka zavolám znova a zjistím, jestli mám tlačítko disablovat. Kód:
--- a/libs/Nette/Forms/Renderers/InstantClientScript.phtml
+++ b/libs/Nette/Forms/Renderers/InstantClientScript.phtml
@@ -75,7 +75,7 @@ nette.validateControl = function(control) {
}
-nette.validateForm = function(sender) {
+nette.validateForm = function(sender, alerts) {
+ if ((typeof(alerts) == "undefined")) var alerts = true;
+
var form = sender.form || sender;
var validators = this.getFormValidators(form);
for (var name in validators) {
@@ -84,7 +84,7 @@ nette.validateForm = function(sender) {
if (form[name].focus) {
form[name].focus();
}
- alert(error);
+ if (alerts) alert(error);
return false;
}
}
A jQuery kód:
$("form").submit(function() {
if (nette.validateForm(this, false)) {
var el = $(this).find(":submit");
el.attr("disabled", "disabled");
setTimeout(function() {
el.attr("disabled", "");
}, 1650);
}
});
Pokud by někoho napadlo systematičtější řešení, sem s ním :)
EDIT: Trochu jsem to zelegantnil, stačí z Nette upravit jen jeden soubor :)
Editoval Ondřej Mirtes (15. 3. 2010 1:02)
- David Grudl
- Nette Core | 8218
Jestli to dobře chápu, problém je v nastavení attributu onsubmit v HTML kódu formuláře?
- Ondřej Mirtes
- Člen | 1536
Abych nedisabloval tlačítko i v případě, že vyskočí alert() s validační chybou, tak uvnitř té metody potřebuji kontrolovat, jestli je formulář validní. A neumím to udělat jinak, než si podruhé zavolat metodu validateForm() :) Což způsobí, že ten alert() vyskočí podruhé. Proto jsem si do té metody validateForm přidal nepovinný atribut alerts, který by se měl jmenovat spíš showAlerts :)
- Honza Kuchař
- Člen | 1662
problém s onSubmit řeší tento doplněk (kvůli tomu vznikl…): https://componette.org/search/?…