Autor Zpráva
Pavel Dumbrovský
Profil *
Zdravím,
udělal jsem si malej skriptít na pusunovaní pořadí fotoalb. Jednoduše kliknu že chci aby se jedno album posunulo o úroveň vejš, tak to tomu albu dá v databázi pořadí - 1 a albu který tam stálo předtím to dá pořadí + 1. Pro identifikaci konkrétního alba přitom beru proměnnou z adresy přes get. Jak ale zabránit tomu, když by uživatel aktualizoval stránku, aby se skript neprovedl znovu?
Philber
Profil
třeba po každém vykonání skriptu, nebo spíš při každém načtení stránky smazat proměnné v kterých se tato informace posílá
unset($promenna)
AM_
Profil
Já to obvykle řeším tak, že data zasílám pouze skriptu, který je na serveru zpracuje (tedy upraví databázi) a následně se přes header("location: ...") přesměruje (zpět) na požadovanou stránku. Považuji to za logicky nejčistší, protože tím oddělíš požadavek na změnu databáze a požadavek na aktualizovanou stránku a při stisknutí F5 se požadavek na databázi znovu neprovede.
AM_
Profil
Philber
Při F5 se ale zašle nový požadavek (stejný jako ten předtím) a kód se spouští znovu se stejnými parametry, tak jaký smysl má mazat proměnné v PHP skriptu, který je v době stisknutí F5 dávno ukončený.
Pavel Dumbrovský
Profil
AM_
Jo, to je řešení, ale zvítězil starej dobrej javascript

            function run(url) {
                setTimeout("top.location.replace('" + url + "');",0);
            }


a

echo "\n<script>run('/admin/postalbum/');</script>\n";


Dík za radu.
Pavel Dumbrovský
Profil
I když mě tak napadá, kdyby byl někdo hodně rychlej a stihl dát F5 v průběhu skriptu, tak by se to teoreticky mohlo provést dvakrát, nebo ne? To platí i pro header..
Nox
Profil
Pavel Dumbrovský
A zvítězi proč? Je to celkem krkolomné a nehezké, řešení od AM_ je mnohem lepší
A bude fungovat i bez JS
Pavel Dumbrovský
Profil
Tak jsem zjistil, že po dvojkliku se to provede dvakrát, asi sem v rici..
imploder
Profil
Pavel Dumbrovský
To, co radí AM_, je dobrá věc a u webových aplikací obvyklý postup. Když se vyvolá skript, co provádí nějakou akci, po provedení akce skript prohlížeč přesměruje na jinou URL, která žádnou akci nevyvolává a např. jen zobrazuj určitý pevně daný výsledek (takže při reloadu se nic nezmění a uživatel není zmaten).

Pojmenujme si skript provádějící akci "akce.php" a skript, na který se pak přesměruje "vysledek.php". Pak to probíhá nějak takhle:
1. Uživatel klikne na tlačítko a prohlížeč pošle serveru požadavek na skript akce.php se zadanými parametry. Pak čeká na odpověď serveru.
2. Server přijme požadavek a začne vykonávat skript akce.php. Akce se provede a skript nakonec vygeneruje HTTP hlavičku* přesměrovávající na vysledek.php (s parametrem např. uspech=ano, který znamená, že se má objevit zpráva o úspěšném provedení akce).
3. Zatímco se akce prováděla, prohlížeč čekal. Jako první věc v odpovědi dostane přesměrovávací hlavičku. Vyžádá si tedy podle ní vysledek.php?uspech=ano. Od serveru mu dojde zpráva o úspěšném provedení akce a prohlížeč ji zobrazí.

*) je to hlavička Location, v php se vygeneruje takhle:
header('Location: '.'http://'.$_SERVER['SERVER_NAME'].dirname($_SERVER['PHP_SELF']).'/'."vysledek.php?uspech=ano");


I když mě tak napadá, kdyby byl někdo hodně rychlej a stihl dát F5 v průběhu skriptu, tak by se to teoreticky mohlo provést dvakrát, nebo ne? To platí i pro header..
To by se s tím řešením dít nemělo, správně by se akce.php vůbec v adresním řádku nemělo objevit. Prohlížeč čeká na první paket odpovědi a až pak začne překreslovat stránku (dřív nemá z čeho) - a jako první věc mu přijde přesměrovávací hlavička, takže se hned přesměruje. Aspoň tak by to mělo správně fungovat.

Tak jsem zjistil, že po dvojkliku se to provede dvakrát, asi sem v rici..
Ano, někdy se stává, že požadavek se odešle, ale serveru trvá odpověď dlouho (protože např. skript se dlouho provádí nebo je špatné spojení). Netrpělivý uživatel (který má před sebou pořád původní stránku s formulářem a ve stavovém řádku něco jako "čekám na odpověď...") mezitím zmáčkne tlačítko znovu a tím odešle další, stejný požadavek a to spustí skript na serveru ještě jednou.

Tímto způsobem na fórech občas dochází k tzv. double postu, tj. stejný příspěvek se vloží dvakrát. Ochrana proti tomu by byla jednoduchá - skript nedovolí uživateli vložil stejný příspěvek na stejné místo, jako poslední, který vložil. Nicméně např. PHPBB ani tohle nemá implementováno, takže se double posty na fórech objevují.

V případě, že aplikace musí uživateli umožňovat provést úplně stejnou akci dvakrát po sobě (to je myslím i tvůj případ s posouváním), je potřeba vymyslet lepší ochranu. Můj návrh je takový: Když se vygeneruje formulář na ovládání toho posouvání, vloží se do něj jako skryté pole i náhodně vygenerovaný kód (můžeš použít PHP funkci uniqid). Po odeslání se společně s akcí uloží i ten kód, který s požadavkem přišel). Skript před provedením akce porovná kód přijatý v požadavku s tím uloženým - když budou stejné, akce se neprovede.

Stejný kód jako uložený totiž znamená, že od předchozího spuštění skriptu nebyl formulář přegenerován (tj. uživatel podruhé klikl na to tlačítko). Když pak uživatel odešle ten formulář dvakrát (a pokaždé je odeslání úspěšné a skript se na serveru spustí, jenom odpovědi to nějak trvá), akce se provede jenom jednou.

Šlo by místo toho javascriptem zabránit v odeslání při druhém kliknutí na tlačítko, ale to není dobré řešení, protože by:
1) znemožnilo odeslání i v případě, že se požadavek např. kvůli výpadku internetu u uživatele nepodařilo odeslat - tohle by uživatele 100% nasralo
2) vyžadovalo javascript
Pavel Dumbrovský
Profil
Díky za vyčerpávající odpověď. Nakonec stejně zůstávám u scriptu:
setTimeout("top.location.replace('" + url + "');",1000);
, poněvadž se mi tam hodí ta prodleva, kdy se na sekundu objeví hláška a pak se to přesměruje (u toho posouvání bez prodlevy). Problém s tím dvojklikem jsem vyřešil následovně: Adresou jsem si posílal jeden parametr, konkrétně album, u kterého mělo dojít k posunutí. V databázi se podle parametru toto album vyhledalo a provedla se změna, jenže právě po dvojkliku to šlo víckrát. Vyřešil jsem to tak, že jsem do adresy přidal druhý parametr pořadí, takže se posílalo album i se svým aktuálním pořadím a pak jsem to ošetřil podmínkou. Když se pošle album, ale jeho současné pořadí už není totožné s pořadím poslaným v adrese, nic se neprovede a funguje to. :) Něco podobného jako popisuješ to řešení s generováním kódu mě taky napadlo, když bych to nemohl ošetřit tou podmínkou s pořadím, tak bych asi musel takto.
Ještě jednou díky za rady!

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: