|(HTTP)|(BROWSER) POST-Formulare und Navigierbarkeit
Christian Seiler
- php
Hallo,
Es gibt ja das allseits bekannte Problem: Wenn ich ein PHP-Script habe, das über POST aufgerufen wird, dann kann der Benutzer nicht so einfach über die Vor/Zurück-Knöpfe des Browser navigieren.
Ein kleines Beispiel, um das ganze zu verdeutlichen:
Ich habe eine Benutzerverwaltung gebaut, die beim Aufruf ohne Parameter eine Liste aller Benutzer anzeigt. Wenn ich nun auf einen Link klicke, um einen Benutzer anzulegen, erscheint das entspr. Formular. Jetzt lege ich einen Benutzer an und (falls Validierung und Einfügen in DB klappt) zeige ich die Meldung "Der Benutzer blablabla wurde erfolgreich angelegt an" und darunter wieder diese Liste aller Benutzer. Wenn ich nun wieder auf den Link "Hinzufügen" klicke, dann erscheint ja wieder mein leeres Formular. Doch dann überlege ich mir es anders und drücke auf den Zurück-Knopf des Browsers. Wenn ich das im IE mache, kommt so ein saublöder Text "Die Seite ist nicht mehr gültig" oder so; der Mozilla fragt mich, ob ich die Daten noch mal senden soll. Ich fange natürlich einen doppelten POST ab (spätestens kurz vor dem Insert sagt er "Benutzer existiert schon"), das ist nicht das Problem; aber das mit der Abfrage im Mozilla bzw. dieser komischen Seite im IE (wo man dann erst auf Aktualisieren drücken soll, damit man noch mal auf die Seite kommt) ist extrem lästig für den Benutzer.
Daher hab' ich mir einen kleinen Workaround ausgedacht, der mit allen Browsern unter denen ich das bisher getestet habe, funktioniert. Ich mache folgendes: Wenn das Formular per POST kommt, dann verarbeite ich die Daten und merke mir, was ich ausgeben will und dann mache ich einen HTTP-Redirect. Das hat zur Folge, dass das 1 History-Eintrag im Browser bleibt und die zweite Seite per GET ausgeliefert wird (und daher der Browser nicht so etwas macht, wie oben geschildert) Soweit ich sehen kann, hat die Methode einen kleinen, jedoch vernachlässigbaren Nachteil: es werden 2 Requests an den Server geschickt. Bei Connection: keep-alive ist das jedoch nicht so sehr problematisch (und das können die meisten; die die es nicht können, warten halt ca. 1/2 sek. länger)
Jetzt hätte ich mehrere Fragen dazu:
1. In der HTTP-Spezifikation steht nichts darüber (es sei denn, ich habe etwas übersehen), was für eine Methode bei einem Location-Header f+r den nächsten Request verwendet werden soll (Ausnahme: bei PUT soll man danach GET verwenden) Hab' ich da was übersehen? Wenn nein (d.h. in der Spezifikation wird das offen gelassen), reagieren alle Browser so? Kann ich mich darauf verlassen?
2. Gibt es bei dieser Methode noch weitere Nachteile?
3. Könnte man das auch anders realisieren?
Grüße,
Christian
[der hofft, dass das nicht zu theoretisch wird]
- In der HTTP-Spezifikation steht nichts darüber (es sei denn, ich habe etwas übersehen), was für eine Methode bei einem Location-Header f+r den nächsten Request verwendet werden soll (Ausnahme: bei PUT soll man danach GET verwenden) Hab' ich da was übersehen? Wenn nein (d.h. in der Spezifikation wird das offen gelassen), reagieren alle Browser so? Kann ich mich darauf verlassen?
RFC 1945 und RFC 2068 haben an sich verboten, dass bei einer Weiterleitung die Methode geändert wird. Es ist auch nicht logisch, das zu tun, da die Resource ja offensichtlich an einer anderen Stelle zu finden ist und dort angefragt werden muss bzw. die Daten dorthin geschickt werden müssen. Darüber haben sich allerdings viele Implementationen hinweggesetzt und verhalten sich, wie du es beschreibst. In RFC 2616 wurde speziell für diesen Zweck die Stati 303 und 307 eingeführt, das kannst du in Abschnitt 10.3.3 auch so nachlesen. Dort findest du auch den Hinweis:
Note: Many pre-HTTP/1.1 user agents do not understand the 303
status. When interoperability with such clients is a concern, the
302 status code may be used instead, since most user agents react
to a 302 response as described here for 303.
- Gibt es bei dieser Methode noch weitere Nachteile?
Schon, das Verhalten der Benutzeragenten ist ja nicht unabsichtlich und du setzt dich darüber hinweg. Das hat ggf. Nachteile für den Anwender.
- Könnte man das auch anders realisieren?
Nicht wirklich.
Hallo,
RFC 1945 und RFC 2068 haben an sich verboten, dass bei einer Weiterleitung die Methode geändert wird. Es ist auch nicht logisch, das zu tun, da die Resource ja offensichtlich an einer anderen Stelle zu finden ist und dort angefragt werden muss bzw. die Daten dorthin geschickt werden müssen. Darüber haben sich allerdings viele Implementationen hinweggesetzt und verhalten sich, wie du es beschreibst. In RFC 2616 wurde speziell für diesen Zweck die Stati 303 und 307 eingeführt, das kannst du in Abschnitt 10.3.3 auch so nachlesen.
Hmmm - dann wäre also Status 303 die logische Antwort auf mein Problem.
Dort findest du auch den Hinweis:
Note: Many pre-HTTP/1.1 user agents do not understand the 303
status. When interoperability with such clients is a concern, the
302 status code may be used instead, since most user agents react
to a 302 response as described here for 303.
Gäbe es denn eine Möglichkeit den 303-"Support" zu testen? Vielleicht nur dann verwenden, wenn der Client über HTTP/1.1 den Request abgesetzt hat?
- Gibt es bei dieser Methode noch weitere Nachteile?
Schon, das Verhalten der Benutzeragenten ist ja nicht unabsichtlich und du setzt dich darüber hinweg.
Die Benutzeragenten verhalten sich ja so, "wie ich es will"; es geht hier ja um den Standard, über den ich mich hinwegsetze (wenn ich 200+Location verwende)
Das hat ggf. Nachteile für den Anwender.
Die wären konkret?
Grüße,
Christian
Gäbe es denn eine Möglichkeit den 303-"Support" zu testen?
Du meinst, innerhalb deines Scripts noch bevor du dem Browser eine Antwort schickst? Nein, das sicher nicht.
Vielleicht nur dann verwenden, wenn der Client über HTTP/1.1 den Request abgesetzt hat?
Das muss ja nicht heissen, dass er mit 303 richtig umgehen kann... aber das wäre ein mögliches Kriterium, wenn dein Webserver dir das mitteilt.
- Gibt es bei dieser Methode noch weitere Nachteile?
Schon, das Verhalten der Benutzeragenten ist ja nicht unabsichtlich und du setzt dich darüber hinweg.
Die Benutzeragenten verhalten sich ja so, "wie ich es will"; es geht hier ja um den Standard, über den ich mich hinwegsetze (wenn ich 200+Location verwende)
Du meinst 302, bei 200 funktioniert hier mit Sicherheit gar nichts mehr.
Hallo,
Vielleicht nur dann verwenden, wenn der Client über HTTP/1.1 den Request abgesetzt hat?
Das muss ja nicht heissen, dass er mit 303 richtig umgehen kann... aber das wäre ein mögliches Kriterium, wenn dein Webserver dir das mitteilt.
Nun ja - das werde ich wohl testen müssen, aber ich denke, dass kein Browser (außer vielleicht IE) damit Probleme hat. Das verwendete Protokoll steht in der CGI-Variable SERVER_PROTOCOL, ist also kein Problem.
Ich würde das ganze dann so machen:
evtl. noch ein Check, ob der Browser bekannt ist als einer, der sich _nicht_ an die Spec hält und dem dann eine 200er mit Location-Header vorsetzen (aber das überlege ich mir noch)
Die Benutzeragenten verhalten sich ja so, "wie ich es will"; es geht hier ja um den Standard, über den ich mich hinwegsetze (wenn ich 200+Location verwende)
Du meinst 302, bei 200 funktioniert hier mit Sicherheit gar nichts mehr.
Ich meine schon 200. Wenn ich in PHP folgendes mache:
Header ("Location: http://www.google.com/");
exit;
dann sieht die _minimalisierte_ Server-Antwort so aus:
HTTP/1.1 200 OK
Location: http://www.google.com/
Jeder Browser, den ich kenne, leitet jetzt weiter (und nimmt immer GET um auf diese Resource zuzugreifen). 302 wollte ich nicht nehmen, da die Resource ja nicht "moved" ist; und die o.g. Methode wird sehr häufig in PHP-Scripts verwendet. (Jetzt sag bloß die ist nicht Standardkompatibel?)
Grüße,
Christian