Formulier opnieuw verzonden na verversen van pagina

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: 1 2 volgende »

Guido  -

Guido -

22/09/2018 14:17:28
Quote Anchor link
Hallo,

Ik heb een contactformulier waarin de afhandeling en het formulier in hetzelfde PHP bestand zitten. Ik gebruik POST variabelen.


Nu heb ik last van het bekende probleem dat na verzending het formulier opnieuw verzonden wordt als ik de pagina ververs. Voor zover ik weet is het niet mogelijk om de POST variabelen leeg te maken direct na verzending, omdat ze nog in het geheugen van de browser zitten.

Heeft iemand hier een idee wat te doen om dit probleem te voorkomen?

Guido
 
PHP hulp

PHP hulp

11/05/2021 16:16:34
 
- Ariën -
Beheerder

- Ariën -

22/09/2018 14:28:07
Quote Anchor link
Het probleem is best simpel op te lossen:

Direct na het POST'en een location-header uitvoeren. Op die manier is de POST-request bij de browser weer verdwenen.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
if($_SERVER['REQUEST_METHOD'] == "POST") {
    // doe je ding...
    header("Location: script.php");
    exit;
}

?>
 
Thomas van den Heuvel

Thomas van den Heuvel

22/09/2018 14:30:15
Quote Anchor link
Gebruik het POST/redirect/GET patroon. Oftewel, zorg dat de verwerkstap een aparte stap is in je script, waarna je ofwel direct wordt doorgestuurd na succesvolle verwerking, ofwel terug wordt gestuurd naar het formulier ingeval van fouten.

Dit komt dus neer in het opsplitsen van de verschillende codedelen en de bijbehorende acties:
- tonen van het formulier (en eventuele terugkoppeling van fouten)
- verwerken van het formulier (en doorsturen na succes of terugsturen na fouten)
- boodschap na succes

EDIT: dit zou dus een moment kunnen zijn om je code eens onder de loep te nemen in termen van paginaopbouw, vooral als je een dynamische website hebt en meerdere pagina's hebt die door PHP worden gegenereerd. Idealiter standaardiseer je dit dus waarbij je al dit soort acties op verschillende pagina's op een uniforme manier kunt behandelen.

Ook zou je dat moment kunnen aangrijpen om de vaste opbouw (het "maintemplate") en pagina-specifieke content uit elkaar te trekken zodat je heel efficiënt je pagina's kunt genereren zonder dat je HTML moet knippen en plakken waarbij je dus dezelfde code dupliceert, die je dan dus vervolgens ook op meerdere plekken zou moeten onderhouden :/.

Hier is PHP bij uitstek voor geschikt, maak hier dan dus ook gebruik van.

De code van @Ariën lost dan wellicht het directe probleem op, maar hiermee hoeft nog geen sprake te zijn van een gestructureerde opbouw. Als dit een standalone script betreft is dit ook niet nodig, maar als je een heleboel (of allemaal) van dat soort pagina's hebt dan wordt het tijd om eens goed na te denken over hoe je deze serveert. Ook als je een wat elegantere foutafhandeling in wilt bouwen (als je die uberhaupt hebt) dan wordt de "paginaflow" belangrijk(er).

Een alternatieve manier zou trouwens een token zijn, die je direct na het POSTen invalideert, zodat alle mogelijke volgende POSTs (die door een refresh werden veroorzaakt) direct afketsen vanwege een ongeldig token. Het gebruik van een token voor formulieren die publiek toegankelijk zijn is sowieso een goed idee omdat dit voorkomt (of het i.i.g. moeilijker maakt) om POST-data rechtstreeks in te schieten vanaf een externe bron. Zo'n token wordt ook wel een CRSF-token genoemd. Dit voorkomt Cross Site Request Forgeries.
Gewijzigd op 22/09/2018 14:45:02 door Thomas van den Heuvel
 
Adoptive Solution

Adoptive Solution

22/09/2018 16:57:12
Quote Anchor link
Ik gebruik dit bovenaan de pagina met het FORM.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<script>
if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
</script>
 
Guido  -

Guido -

22/09/2018 22:25:04
Quote Anchor link
Hoi,

Bedankt voor jullie reacties. Ik ga alle opties eens proberen.

Met de header location heb ik trouwens de ervaring dat ik de melding "Warning: Cannot modify header information - headers already sent by" krijg. Maar dat is mogelijk niet te voorkomen omdat ik zowel formulier als afhandeling in hetzelfde bestand heb staan.

Wat het Post/Redirect/Get patroon betreft, heb je zo een voorbeeld van een (eenvoudig) script die op deze wijze gebouwd is? Dan kan ik eens goed kijken hoe het precies werkt.

Guido
 
- Ariën -
Beheerder

- Ariën -

22/09/2018 23:28:01
Quote Anchor link
Als je "Warning: Cannot modify header information - headers already sent by... te zien krijgt, dan heb je blijkbaar output in de vorm van HTML of een zichtbare witruimte voor je header()-functies staan, en dat mag niet. De foutmelding geeft ook aan in welk bestand dit gebeurt, en na de dubbele punt op welke lijn dit plaatsvindt. Als je jouw script zo aanpast dat alle taken eerst worden uitgevoerd in het begin, zonder output te hebben, dan kan het prima.

En het voorbeeld van het POST/redirect/GET patroon heb ik al gegeven.
Gewijzigd op 22/09/2018 23:29:05 door - Ariën -
 
Thomas van den Heuvel

Thomas van den Heuvel

23/09/2018 01:17:52
Quote Anchor link
Er mag best HTML voor de header() functies staan, zolang deze maar niet verstuurd wordt voordat je klaar bent met het instellen van headers. Het kan namelijk best zo zijn dat alle output automatisch gebufferd (opgespaard) wordt en dan loop je nooit tegen deze foutmeldingen aan.

Output buffering kan best meerwaarde hebben, maar om het nu enkel te gebruiken om "headers already sent" meldingen onder het tapijt te vegen is natuurlijk niet echt een goed ontwerpbeginsel, en het spoort je ook niet aan om dingen in de goede volgorde te zetten.

Ik ben nog steeds van mening dat op het moment dat je een pagina/script verschillende dingen wilt laten doen, je deze acties echt zou moeten gaan compartimenteren in aparte onderdelen. Dit omdat anders het gevaar van spaghetti code constant op de loer ligt. Daarnaast heeft het scheiden van acties in verschillende stappen ook het bijkomende voordeel dat je deze acties ook in afzondering kunt debuggen. En ook is dit -wanneer je steeds meer interactie inbouwt in code die onderhand hele HTML-documenten uitspugen- ongeveer het moment dat je na moet gaan denken over een soort van algemene aanpak voor document-opbouw. Zelfs als dit een standalone script is loont het de moeite om een vaste aanpak te volgen. Je kunt namelijk telkens dezelfde code als basis gebruiken, en vervolgens kun je heel snel documenten, en op den duur ook websites, in elkaar zetten. Simpelweg omdat je elke keer hetzelfde sjabloon gebruikt.

Ik weet niet hoe je OOP-kennis is, maar mogelijk biedt dit artikel enige inspiratie.
 
- Ariën -
Beheerder

- Ariën -

23/09/2018 01:43:20
Quote Anchor link
Maar outputbuffering staat standaard gewoon uit. Ook in dit geval, anders had Guido die headers already sent by melding nooit gekregen.
 
Guido  -

Guido -

23/09/2018 13:22:02
Quote Anchor link
Een formulier opbouwen via het POST/redirect/GET patroon is natuurlijk de meest solide oplossing.

Ik snap het POST/redirect/GET patroon nu grotendeels, mede dankzij dit eenvoudige voorbeeld:
http://codexfocus.com/postredirectget-example/

Even voor de duidelijkheid, de "header location" alleen gebruiken in het geval van een succesvolle verzending. Nooit de terugmelding van de foutieve input via "header location" doen, omdat je dan alle (foutieve) input weer kwijt bent en het formulier opnieuw kunt invullen. Correct?

Guido
 
- Ariën -
Beheerder

- Ariën -

23/09/2018 13:38:51
Quote Anchor link
Klopt, of je moet het in een sessie plaatsen.

Ik ieder geval zou ik aanraden om een beveiliging tegen CSRF in te bouwen en het besproken doorstuur-patroon.
 
Guido  -

Guido -

23/09/2018 14:04:00
Quote Anchor link
Hoi,

Het wordt dus weer complexer.

1) In bestand form.php staat mijn formulier met als actie: action="validate.php".
2) De validatie van mijn formulier vindt dus plaats in bestand validate.php.
3) Indien validatie juist is, dan stuur ik terug naar form.php via header("location:form.php?m=success").
4) In form.php wordt dan via GET het Dank U bericht gegenereerd.
Voor de bezoeker gebeurt alles dus netjes op dezelfde pagina.

Maar in het geval van fout ingevuld veld, moet ik dus een sessie gebruiken om de ingevulde velden te "onthouden"?
Is er geen andere mogelijkheid, zonder gebruik van sessies? Ik wil een sessie juist voorkomen, het formulier zo eenvoudig mogelijk laten.

Guido
 
- Ariën -
Beheerder

- Ariën -

23/09/2018 14:13:28
Quote Anchor link
Een sessie hoeft niet, zoals ik al zei.
Want alles zit nog in je $_POST. Tenzij je een location-header gebruikt.

Persoonlijk raad ik aan om alles in een bestand af te handelen. Als je wilt zal ik straks even een opzetje maken.
Gewijzigd op 23/09/2018 14:15:15 door - Ariën -
 
Guido  -

Guido -

23/09/2018 14:22:38
Quote Anchor link
Een opzetje voor me maken vind ik sowieso niet erg ;-)

Het gaat mij er dus om hoe ik ná de afhandeling weer terug ga naar form.php en daar mijn foutmelding toon, en dus zonder gebruik te maken van de header location.

Maar als jij een voorbeeld hebt om alles toch in hetzelfde bestand af te handelen (en mét oplossing van de pagina verversing), ik ben benieuwd.
 
Adoptive Solution

Adoptive Solution

23/09/2018 14:44:24
Quote Anchor link
Hier voorbeeld om werking van JS te zien :

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
if ( $_REQUEST["submit"] ) {
    // alles afhandelen
    echo "<pre>";
    print_r($_POST);
    echo "</pre>";
}

?>

<html>

<head>

<script>
if ( window.history.replaceState ) {
    // haal comment volgende regel weg om het effect te zien van een herlaad pagina
    //window.history.replaceState( null, null, window.location.href );
}
</script>

</head>

<body>

<form method="POST">
    <input type="text" name="tekst">
    <input type="submit" name="submit" value="submit">
</form>
    
</body>

</html>


Is allemaal te vinden op de interwebs.
 
- Ariën -
Beheerder

- Ariën -

23/09/2018 15:01:26
Quote Anchor link
Maar JavaScript is niet alles. Je zult het ook serverside moeten oplossen met een redirect in PHP.
Je kan in website prima met $_GET een aparte actie maken waarheen je doorgestuurd wordt als alles goed is gegaan.
 
Thomas van den Heuvel

Thomas van den Heuvel

23/09/2018 16:09:42
Quote Anchor link
Quote:
Even voor de duidelijkheid, de "header location" alleen gebruiken in het geval van een succesvolle verzending. Nooit de terugmelding van de foutieve input via "header location" doen, omdat je dan alle (foutieve) input weer kwijt bent en het formulier opnieuw kunt invullen. Correct?

Ja en nee. Omdat je de acties splitst heb je dus nu een middel nodig om informatie over te hevelen. Een actie die "geen output produceert" zoals de verwerkstap van een formulier zal iemand altijd op een of andere manier doorsturen. Dit is juist de crux van POST/redirect/GET, zodat je nooit meer terug kunt naar de POST-stap. Dit kan dus ook inhouden dat je de foutieve informatie (middels een sessie) onthoudt en de gebruiker terugstuurt naar een formulier. Een alternatieve manier is dat je het formulier submit via AJAX, in welk geval je de pagina niet (geheel) verlaat (EDIT: maar ook daar zul je maatregelen moeten treffen om dubbelposts tegen te gaan).

Quote:
Het gaat mij er dus om hoe ik ná de afhandeling weer terug ga naar form.php en daar mijn foutmelding toon, en dus zonder gebruik te maken van de header location.

In de verwerkstap wordt besloten waar je naartoe wordt (terug)gestuurd. Hoe je vervolgens je data over meerdere pagina's vasthoudt is een apart probleem dat je dus ook apart zult moeten oplossen. Zoals aangegeven maakt het "redirect" deel uit van de oplossing, ook als je teruggaat naar het formulier. In deze opzet heb je gewoon altijd headers nodig, en dat introduceert de noodzaak om op een of andere manier de invoer te "redden" zodat deze weer teruggeplaatst kan worden in het formulier. De enige (?) manier om dit zonder headers te doen is via AJAX je formuliersubmits afhandelen.

Niemand heeft ooit gezegd dat een gestructureerde oplossing makkelijker zou zijn, maar als je meer/heel veel met pagina's met deze opzet werkt, dan heb je deze extra administratie zo terugverdiend.

EDIT: wat ontbreekt in het zeer korte artikel waar je eerder naar linkte was nagaan of er inderdaad sprake is van een form submit, en ook een validatie van de velden met op zijn minst een controle op het bestaan hiervan. Heb je het artikel waar ik naar linkte ook gelezen?
Gewijzigd op 23/09/2018 16:18:29 door Thomas van den Heuvel
 
Guido  -

Guido -

23/09/2018 18:05:44
Quote Anchor link
Hoi,

Ik heb het artikel wel gelezen maar het werd op den duur te complex voor mij.

Misschien is de meest eenvoudige oplossing om toch te werken met een sessie om de input waarden te onthouden.

Dit is een werkend voorbeeld geworden (typfout voorbehouden) bestaande uit het formulier (form.php) en afhandeling (validate.php) en dus volgens het POST/redirect/GET patroon.

Form.php:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
// Start sessie
if (!isset ($_SESSION)) session_start();
?>

<html>
<body>
<head>
</head>
<body>
<div>
    // Toon resultaat
    <?php
    $check
= $_GET['m'];
    if ($check == error) {
        echo 'Fout bericht';
    }

    if ($check == success) {
        unset($_SESSION['name']);
        echo 'Succes bericht';
    }

    ?>

    // formulier    
    <form action="validate.php" method="POST">
        <label>Naam:</label>
        <input name="name" type="text" value="<?php echo $_SESSION['name'] ?>" >
        <input type="submit" value="Versturen" >
    </form>
</div>
</html>
</body>


Validate.php:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
// Start sessie
if (!isset ($_SESSION)) session_start();

// Formulier afhandeling
if($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Variabelen
    $name = strip_tags($_POST['name']);

    // Sessie variabelen
    $_SESSION['name'] = $name;

    // Valideer invoer
    if( empty($name) ) {
        // Fout
        header("location:form.php?m=error");
    }
else {
        // verstuur bericht enzo

        // Succes

        header("location:form.php?m=success");
    }
}

?>


Graag je reactie.

Guido
Gewijzigd op 23/09/2018 18:12:13 door Guido -
 
- Ariën -
Beheerder

- Ariën -

23/09/2018 18:17:25
Quote Anchor link
Vergeet de exit() niet na de location-header, je script moet ook stoppen.
Verder ozu ik je strip_tags() uitvoeren op het moment dat je de inhoud ophaalt, i.p.v. dat je het erin plaatst. Zorg dat deze altijd rauw is.

En waarom in twee bestanden? Je kan toch prima in één bestand werken? Veel overzichtelijker :-)
Gewijzigd op 23/09/2018 18:18:09 door - Ariën -
 
Guido  -

Guido -

23/09/2018 18:22:51
Quote Anchor link
Dank voor je reactie.

Wat je laatste opmerking betreft, daar zijn overduidelijk de meningen over verdeeld ;-)

Guido
 
Adoptive Solution

Adoptive Solution

23/09/2018 18:26:21
Quote Anchor link
Hiero,

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
if( $_SERVER['REQUEST_METHOD'] == 'POST') {
    $name = strip_tags($_POST['name']);
    if( empty( $name ) ) {
        $m = "error";
    }
else {
        $m = "success";
    }
}

?>

<html>
<body>
<head>
<script>
if ( window.history.replaceState ) {
    //haal comment volgende regel weg om het effect te zien van een herlaad pagina
    window.history.replaceState( null, null, window.location.href );
}
</script>
</head>
<body>
<div>

<?php
if ( $m == "error" ) {
    echo '<p>Fout bericht</p>';
}
elseif ($m == "success") {
    echo '<p>Succes bericht</p>';
}

?>


<form method="POST">
    <label>Naam:</label>
    <input name="name" type="text" autofocus value="<?php echo $name; ?>" >
    <input type="submit" value="Versturen" >
</form>

</div>

</html>

</body>
 
- Ariën -
Beheerder

- Ariën -

23/09/2018 18:26:39
Quote Anchor link
Nu nog wel, maar uiteindelijk wordt je applicatie steeds lastiger te doorgronden als je alles in aparte bestanden plaatst, zonder dat je weet of waar en of er samenhang is tussen bestanden.
 

Pagina: 1 2 volgende »



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.