Zamezení vícenásobného odeslání formuláře JavaScriptem

7. září 2004

V řadě webových aplikací je nežádoucí, aby uživatel mohl odeslat údaje z formuláře vícekrát. V této ukázce vyřešíme problém, kdy může uživatel vědomě nebo nevědomě odeslat více požadavků opakovaným kliknutím na odesílací tlačítko předtím, než dostane ze serveru odpověď.

Pokud není při odesílání formuláře odezva ze serveru dostatečně rychlá, uživatel má tlačítko stále k dispozici, i když na něj již kliknul pro odeslání. V této době, od prvního kliknutí až po okamžik, kdy tlačítko „zmizí“ načtením výsledku zpracování formuláře, tak každým dalším kliknutím uživatel vyvolá nový request a tento je přijat a zpracován serverem. Marně se potom můžeme divit, proč nám obsah formuláře dorazil do mailu 5x nebo si zákazník 3x objednal totéž zboží.

Následující řešení není samozřejmě všelékem, je to jen drobné vylepšení pomocí klientského skriptu – i to však může podchytit alespoň 80 % pokusů o vícenásobné odeslání týchž údajů (tam, kde má klient zapnutý JavaScript). Hodí se proto pro všechny aplikace, které provádějí inserty do databáze, vkládají příspěvky do diskusí, odesílají objednávky nebo třeba hodnocení ankety.

Tento doplněk může zabránit ještě většímu přetížení serveru v případě, že jsou ze serveru pomalé odezvy a netrpěliví uživatelé pak opakovanými requesty situaci ještě zhoršují. Je potřeba si však říci, že řešení není stoprocentní. Je závislé na JavaScriptu, proto tam, kde potřebujeme absolutní jistotu, že nebude zpracováván duplicitní požadavek, je třeba nasadit jiné mechanismy, případně je skombinovat s tímto. Jedním z možných postupů je například ve skrytém poli formuláře předávat unikátní identifikátor a do databáze povolit uložení takového záznamu pouze jednou – možností je více, detaily však překračují rozsah tohoto článku.

JavaScriptem můžeme těsně před zahájením odesílání provést různé operace, stačí je navázat jako obsluhu onsubmit formuláře. Vcelku triviálním řešením je zakázání odesílacího tlačítka (nastavit vlastnost disabled na „true“). Toto řešení se ovšem hodí jen pro případy, kdy nás nezajímá, na které tlačítko ve formuláři uživatel kliknul – pokud totiž tlačítko zakážeme, neodešle se na server ani jeho hodnota, což je v řadě aplikací nežádoucí. Naše řešení tedy nebude tlačítka zakazovat, pouze stornuje událost kliknutí. Můžeme také doplnit hlášení pro uživatele, které ho bude informovat, že formulář se již zpracovává a že není třeba klikat na tlačítko opakovaně. Ukázka (zdrojový kód) je tentokrát spíše symbolická, pokud máte rychlé připojení, pravděpodobně se vám nepodaří nasimulovat tak rychlé opakované kliknutí, aby aplikace zareagovala upozorňujícím alertem.

Základem aplikace je ukázkový formulář s více tlačítky:

<form method=“post“ action=“#“ onsubmit=“__formDisMultipleClick(this);“ id=“__aspnetForm“>
  <div><input type=“text“ name=“TextBox1″ /></div>
  <div>
    <input type=“submit“ name=“Send1″ value=“Odeslat 1″ />
    <input type=“submit“ name=“Send2″ value=“Odeslat 2″ />
    <input type=“submit“ name=“Send3″ value=“Odeslat 3″ />
    <input type=“hidden“ name=“__EVENTTARGET“ value=““ />
    <input type=“hidden“ name=“__EVENTARGUMENT“ value=““ />
  </div>
</form>

Vidíme, že ve formuláři je i ukázkové textové pole a skrytá formulářová pole, která obyčejně využívá řada webových aplikací. Na událost onsubmit je obvykle navázána kontrola formuláře, což by nemělo bránit přidání zpracování i naší funkce pro zamezení vícenásobného odeslání – volání jednotlivých funkcí oddělíme středníkem.

V naší ukázce má na starosti hlídání odesílání funkci __formDisMultipleClick(). Její důvtipnost spočívá v tom, že si sama najde všechna tlačítka typu submit. Do stránky tedy stačí přidat naši funkci a o tlačítka se nemusíme nijak zvlášť starat, i když nějaké přidáme nebo odebereme, funkce si s tím automaticky sama poradí.

function __formIsSending()
{
  alert(‚Strpení prosím, požadavek se již odesílá…‘);
  return false;
}
function __formDisMultipleClick(formObj)
{
  for (var i=0; i<formObj.length; i++ )
  {
    if (formObj.elements[i].type == ‚submit‘ || formObj.elements[i].type == ‚image‘)
    {
      formObj.elements[i].onclick = __formIsSending;
    }
  }
}

Uvedená funkce v cyklu projde všechny elementy objektu formuláře (je předán v parametru formObj) a na ty, které jsou typu submit nebo image, naváže obsluhu události onclick jako funkci __formIsSending(). Úkolem této funkce je „stornovat“ kliknutí na tlačítko, což je zajištěno předáním návratové hodnoty „false“ (return false;). Jako již zmíněný doplněk je použito vyskakovacího hlášení alert(), které informuje uživatele, že formulář se již odesílá. V cyklu je tato funkce navázána na všechna odesílací tlačítka při prvním odeslání formuláře. Veškerá další kliknutí již způsobí pouze zobrazení hlášení, k dalšímu odeslání formuláře (a tedy ani jeho obsluhy onsubmit) nedojde.

Aplikace pro jednoduchost a názornost neřeší všechny možné případy, například odesílání formuláře stisknutím tlačítka „enter“ při editaci textového pole nebo zvlášť klientským skriptem, protože zamezit vícenásobnému odeslání je v těchto případech komplikovanější. Určitou možností by zde bylo stornovat přímo událost odesílání formuláře – mohli bychom na onsubmit navázat funkci, která bude prostě vracet „false“, obdobně, jako jsme funkci navázali na odesílací tlačítka. Není to však úplně nejjednodušší, protože zde vyvstává otázka, kdy přesně obsluhu události odesílání formuláře přenastavit, aby vůbec bylo možné alespoň poprvé formulář odeslat.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *