portseven: Erneute Formular-Bestätigung verhindern

Tag,

ich wollte bei meiner Seite eine Page haben wo alle User oder Beiträge angezeigt werden.

Es gibt eine Select Option wo paar Städe stehen & ein Submit button.

Das Problem ist, wenn ich zurück oder vor möchte steht da immer

Wisst ihr wie man es verhindern kann?

So sieht mein Code aus:

<ul class="nav_select">
<li>
		<form action="" method="POST">
		<select name="city">
		<option value="all">Stadt W&auml;hlen</option>
		<option value="berlin">Berlin</option>
		<option value="hamburg">Hamburg</option>
		</select>
		</li>
		</ul>

		<ul>
			<li><button type="submit" name="submit">Mitglieder</button></li>
			</form>
		</ul> `

<?php if(isset($_POST['submit'], $_POST['city'])) { ?>

switch($_POST['city']) {

case 'berlin':
$city = 'berlin';
break;

case 'hamburg':
$city = 'hamburg';
break;
}

	$stmt = $pdo->prepare("
	SELECT
	user.id,
	user.username,
	user.image,
	user.city
	FROM user
	
	WHERE user.id != :user AND city = :city");
	$stmt->BindParam(':user', $_SESSION['id']);
	$stmt->BindParam(':city', $city);
	
	if(!$stmt->execute()) {
	print_r($stmt->errorInfo());
	} ?>

	<article id="user">

	<?php while($row = $stmt->fetch()) { ?>
	
  <p><?php echo $row['username']; ?></p>
	
	<?php } ?>

	</article>

<?php } ?>

  1. Hallo portseven,

    https://wiki.selfhtml.org/wiki/PHP/Tutorials/Reloadsperre

    Bis demnächst
    Matthias

    --
    Rosen sind rot.
  2. Hallo portseven,

    die Semantik der HTTP-Verben GET und POST ist so, dass ein GET idempotent ist - d.h. ein mehrfacher GET auf die gleiche URL liefert immer das gleiche Ergebnis und ändert nichts an den gespeicherten Daten des Servers (abgesehen von Session-DB und Logs). Ein POST ist es dagegen nicht, er sendet Daten mit der Absicht, die auf dem Server gehaltenen Daten zu verändern.

    Die Grenze mag ab und zu fließend sein, vor allem dann, wenn ein GET eine Momentaufnahme einer veränderlichen Datenbasis liefert (Aktienkurse), aber die Grundidee bleibt: Einen GET kann ich gefahrlos mehrfach ausführen, einen POST nicht.

    Deswegen ist ein POST für dein Formular das falsche HTTP Verb, du änderst nichts, du sendest nur Filterwerte. Gib deinem form-Element das Attribut method="GET" und das Problem ist gelöst.

    Aber Vorsicht: Das ist kein Allheilmittel. Zum einen wird durch dieses Verfahren die Datenübertragung an den Server geändert - die Parameter stehen nun in $_GET statt $_POST (weswegen Du sie besser aus $_REQUEST ausliest, dieses Superglobal fasst $_GET, $_POST und $_COOKIE zusammen). Zum anderen steht der Parameter nun sichtbar in der URL der Folgeseite - was aber in deinem Fall überhaupt kein Problem ist - eher ein Vorteil, weil man das Filterergebnis nun als Favorit speichern kann.

    UND DRITTENS: Wenn Du auf Grund der Formulareingabe Updates in deiner Datenbank machst, dann ist ein erneuter Post tatsächlich kritisch und die Browserwarnung erfolgt aus gutem Grund. Dann sind die von Matthias verlinkten Verfahren anzuwenden, um dem Anwender zumindest eine kleine Chance zu lassen.

    Ich habe es noch nicht ausprobiert; aber eventuell hat man mit dem History-API in JavaScript eine Chance, beim Klick auf Zurück den POST-Request zu überspringen.

    Rolf

    --
    sumpsi - posui - clusi
    1. Tach!

      Aber Vorsicht: Das ist kein Allheilmittel. Zum einen wird durch dieses Verfahren die Datenübertragung an den Server geändert - die Parameter stehen nun in $_GET statt $_POST (weswegen Du sie besser aus $_REQUEST ausliest, dieses Superglobal fasst $_GET, $_POST und $_COOKIE zusammen).

      Man muss aus $_GET und Konsorten nichts auslesen. Das sind bis auf die superglobale Eigenschaft ganz normale Variablen, die man direkt und ohne Umwege verwenden kann.

      Zudem ist $_REQUEST nicht die bessere Alternative. Man sollte genau die Variable nehmen, in der man den Wert erwartet. Zudem ist der Default-Wert von request_order leer. Diese Direktive gibt an, in welcher Reihenfolge $_REQUEST befüllt wird. Da sind also gar keine Werte drin. Oder man nimmt eine der ausgelieferten php.ini, dann ist der Wert GP, also ohne Cookies. Das heißt, im Zweifelsfall hat man da ein undefiniertes Verhalten.

      dedlfix.

      1. Hallo dedlfix,

        Man muss aus $_GET und Konsorten nichts auslesen

        Jeder von uns hat so seine Reflexe. Gunnar bei A11Y-Issues, und Du, wenn Inhalte von Superglobals in lokale Variablen kopiert werden. Aber diesmal hast Du zu schnell geschossen; ich meinte tatsächlich auslesen, nicht kopieren, und eine Variable, die ich nicht auslese, ist sinnlos.

        Übrigens: die PHP Doku sagt auf der Seite von $_REQUEST:

        The presence and order of variables listed in this array is defined according to the PHP variables_order configuration directive.

        WTF? Das wurde die Doku nach 5.3 nicht aktualisiert; seit dem ist zunächst mal request_order, und nur wenn der Wert leer ist, zieht die variables_order. Der Default für request_order ist zwar leer, um Rückwärtskompatibel zu sein, aber die mitgelieferten Muster-INIs von PHP setzen "GP" ein. Auch das ist etwas, das nicht auf der Dokuseite steht. Grmbl...

        Du sagst also, es ist ungünstig, $_REQUEST zu verwenden. Eigentlich finde ich es ganz praktisch, seine Seiten Requesttype-agnostisch zu bauen. Wie meine php.ini aussieht, weiß ich ja normalerweise. Ich habe den Hinweis jetzt nur für $GLOBALS gefunden: Wird $_REQUEST ebenfalls lazy initialisiert, so dass ich durch den Nichtzugriff darauf Zeit sparen kann?

        Rolf

        --
        sumpsi - posui - clusi
        1. Tach!

          Jeder von uns hat so seine Reflexe. Gunnar bei A11Y-Issues, und Du, wenn Inhalte von Superglobals in lokale Variablen kopiert werden. Aber diesmal hast Du zu schnell geschossen; ich meinte tatsächlich auslesen, nicht kopieren, und eine Variable, die ich nicht auslese, ist sinnlos.

          Lesen/Lesezugriff wäre das bessere Wort. Auslesen ist eher von der Bedeutung herausholen, erstmal auspacken vor dem Verwenden.

          Übrigens: die PHP Doku sagt auf der Seite von $_REQUEST:

          The presence and order of variables listed in this array is defined according to the PHP variables_order configuration directive.

          WTF? Das wurde die Doku nach 5.3 nicht aktualisiert; seit dem ist zunächst mal request_order, und nur wenn der Wert leer ist, zieht die variables_order. Der Default für request_order ist zwar leer, um Rückwärtskompatibel zu sein, aber die mitgelieferten Muster-INIs von PHP setzen "GP" ein. Auch das ist etwas, das nicht auf der Dokuseite steht. Grmbl...

          Zumindest steht es bei der Description of core php.ini directives.

          Den Satz "If this directive is not set, variables_order is used for $_REQUEST contents" hatte ich überlesen. Da kann also auch noch mehr Varianten haben, die je nach Konfiguration anders sind.

          Du sagst also, es ist ungünstig, $_REQUEST zu verwenden. Eigentlich finde ich es ganz praktisch, seine Seiten Requesttype-agnostisch zu bauen. Wie meine php.ini aussieht, weiß ich ja normalerweise.

          Ja, du vielleicht. Ich weiß aber auch nicht alle Einstellungen aus dem Kopf und weniger affine Personen wissen das gleich gar nicht. Besonders hat man oftmals kaum Einfluss, wenn man Software für Dritte schreibt, deren Umgebung man nicht kennt und/oder beinflussen kann.

          Requesttype-agnostisch kann auch Nachteile haben. Beispielsweise wäre eine Konfiguration CGP oder CPG eine nicht ungefährliche Sache, weil dabei Cookie-Werte durch GET- und POST-Werte überschrieben werden können, und man so Links erstellen kann, die Cookies überschreiben, auf die man sonst als Außenstehender keinen Einfluss hätte.

          Ich habe den Hinweis jetzt nur für $GLOBALS gefunden: Wird $_REQUEST ebenfalls lazy initialisiert, so dass ich durch den Nichtzugriff darauf Zeit sparen kann?

          auto_globals_jit gilt für "SERVER, REQUEST, and ENV". Wird $GLOBALS überhaupt initialisiert? Ist das nicht nur eine Sicht auf den Variablenspeicher?

          dedlfix.

          1. Hallo dedlfix,

            ok, von CGP würde ich auch abraten. Insofern hast Du recht, diese Einstellungen sind eine Foot Gun.

            Cookies würde ich eh immer über $_COOKIES lesen, bei $_GET und $_POST sehe ich es weniger kritisch.

            Aber jetzt, nachdem wir was drüber geschnackt haben, denke ich mir: Es braucht vermutlich mehr Hirnschmalz, um $_REQUEST sauber aufzusetzen, als man bei der Verwendung spart...

            Rolf

            --
            sumpsi - posui - clusi
            1. hi Rolf,

              Aber jetzt, nachdem wir was drüber geschnackt haben, denke ich mir: Es braucht vermutlich mehr Hirnschmalz, um $_REQUEST sauber aufzusetzen, als man bei der Verwendung spart...

              Lt. RFC ist auch gar nicht die Request-Methode dafür maßgeblich ob ein Body gesendet wurde sondern die Angabe in Content-Length.

              MfG

              1. Hallo pl,

                So wie ich die RFC lese, lässt sich das mit einem klaren "Lass das" beantworten:

                A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

                GET-Body: to boldly go where no man has gone before... 😂

                Rolf

                --
                sumpsi - posui - clusi
                1. hi

                  So wie ich die RFC lese, lässt sich das mit einem klaren "Lass das" beantworten:

                  A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

                  GET-Body: to boldly go where no man has gone before... 😂

                  Na dann prüf mal Deine Webserver ob sie entsprechend konfiguriert sind 😉

                  Ansonsten gilt:

                  The presence of a message-body in a request is signaled by the inclusion of a Content-Length or Transfer-Encoding header field in the request's message-headers.

                  RFC2616. Von CGI.pm jahrzehntelang ignoriert 😉

                  PS:

                  my $r = HTTPsRequest->common(
                      host    => "forum.selfhtml.org",
                      uri     => "/self",
                      method  => "GET",
                      content => "asdf"
                  ) or die $@;
                  
                  print Dumper $r->response_header(); # 200 OK
                  
        2. hi,

          Eigentlich finde ich es ganz praktisch, seine Seiten Requesttype-agnostisch zu bauen.

          Ich auch wobei Transparenz hierfür der passendere Begriff ist. Und noch mehr Transparenz ergibt sich, wenn eine Parameter~Kontrollstruktur außer von der Requestmethode auch vom Content-Type unabhängig ist (Es sei denn der Programmablauf gibt das zwingend vor).

          Dann sind diese Layer nämlich austauschbar ohne den ganzen Code ändern zu müssen. Voraussetzung dafür ist natürlich ein Parser der anhand des gesendeten Enctype parsen kann. Also egal ob ein Request mit GET oder POST oder mit enctype="application/x-www-form-urlencoded" oder Content-Type: application/json reinkommt. Wenn wir bessere Browser hätten... aber mit fetch() ist sowas alles schon machbar.

          MfG

    2. Danke euch erstmal für die Hilfe.

      Ab & zu muss ich aber $_POST verwenden.

      Sollte ich dann einfach dies benutzen: https://wiki.selfhtml.org/wiki/PHP/Tutorials/Reloadsperre ?

      1. Tach!

        Ab & zu muss ich aber $_POST verwenden.

        Dann nimmst du ab und zu eine der Maßnahmen, zum Beispiel das Weiterleiten auf einen GET-Request nach dem POST. Generell solltest du dich an die genannte Faustregel halten: Abfragen mit GET und Änderungen mit POST.

        Auch bei Szenarien, wo es dir einfach erscheint, wie dem Löschen eines Datensatzes. Dazu braucht man nur dessen ID und die könnte man einfach an ein GET hängen. Ja, aber nur, um die Bestätigungsseite aufzurufen. Das eigentliche Löschen sollte mit einem POST-Request erfolgen. Ein wesentlicher Grund ist, dass (Such-)Maschinen GET-Requests verfolgen und dir deine Daten ändern, wenn die mit einfachem GET-Request manipulierbar sind.

        dedlfix.