Das HTTP Protokoll ist ein synchrones Client-Server Protokoll mit sieben Methoden. Jede Methode impliziert eine bestimmte Semantik die von der WWW-Infrastruktur insbesondere für Optimierung durch Caching und Client-Recovery genutzt wird:
Methode | Bedeutung | idempotent | safe |
---|---|---|---|
GET | Fordert den Inhalt einer Ressource an. Der Aufruf darf keine Zustandsänderung am Server hinterlassen. | ja | ja |
POST | Ressourcen werden im Server geändert. | nein | nein |
HEAD | Sendet nur die Headerinformationen einer Ressource. | ja | ja |
PUT | Dem Server werden Ressourcen hinzugefügt, dabei bleiben alle vorher existierenden Ressourcen unverändert. | ja | nein |
DELETE | Vom Server werden Ressourcen genommen, dabei bleiben alle anderen Ressourcen unverändert. | ja | nein |
OPTIONS | Der Server sendet dem Client alle Optionen einer Ressource. | ja | ja |
TRACE | Trace Info: alle Infrastrukturkomponente hinterlassen im Request-Response Pfad eine Spur. | ja | ja |
Idempotent bedeutet: die Methode kann Änderungen am Zustand des Servers verursachen, jedoch der mehrmalige Aufruf der gleichen Methode ändert das Ergebnis nicht. Bei einer idempotenten Methode müssen Client und Server keine Vorkehrungen treffen, um einen versehentlichen Mehrfachaufruf zu vermeiden. Eine Methode ist safe, wenn sie keine Veränderung im Zustand des Servers hinterlässt. Hier hat der Client die Möglichkeit, das Ergebnis einer Anfrage nach bestimmten, zwischen Client und Server ausgehandelten Regeln, zu cachen. Es ist Aufgabe des Designs, die Anwendungsfälle einer Webanwendung auf die richtigen Methoden des HTTP Protokolls zu verteilen. Auffällig: GET ist die einzige Methode bei der die Repräsentation einer Ressource geliefert wird. Hier kommt das PRG Pattern ins Spiel.
Das POST-Redirect-GET (PRG) Pattern regelt die semantisch korrekte Verwendung von POST und GET Anfragen:
Welcher HTTP Statuscode ist der richtige?
Anscheinend ist die Antwort schnell gefunden: HTTP 303 See Other beschreibt genau das Verhalten, das für das PRG-Pattern benötigt wird. Diesen Status gibt es aber erst seit HTTP1.1 und vorher, mit HTTP1.0, war HTTP 302 Moved Temporarily die einzige Wahl. HTTP Clients haben auf diesen Status im Sinne des PRG Patterns reagiert. Aus dem HTTP 302 Moved Temporarily wurde dann mit HTTP1.1 ein HTTP 302 Found und ein HTTP 303 See Other. Laut Spezifikation ist eigentlich HTTP 302 Found nicht für das PRG Pattern geeignet, die Änderung der Methode und eine automatische Weiterleitung sind nicht gestattet! Dennoch wurden HTTP Clients weiter so implementiert, dass sie auf den Statuscode HTTP 302 Found im Sinne des PRG Patterns reagieren. Die Antwort auf die gestellte Frage lautet also:
PRG Pattern und temporärer Zustand
In der Praxis ist die sauber Umsetzung des PRG Patterns eine Herausforderung. Zweck einer POST Anfrage ist immer, den Zustand der Anwendung zu verändern. Ein einfaches Beispiel ist das gelungene Update in einer Datenbanktabelle, eine persistente Zustandsänderung. Endet die POST Anfragen mit einem Fehler ist die Sache weniger klar. Der Client soll natürlich eine Fehlernachricht bekommen - ohne PRG Pattern hätte man den Fehler einfach als Response der POST Anfrage gerendert. Beim PRG Pattern ist eine Fehlermeldung eine View mit Parametern, die erst nach einem Redirect über eine GET Anfrage zum Client gesendet wird.
Das Problem besteht offensichtlich darin, dass eine solche View nichtpersistente Daten darstellen soll, im beschriebenen Beispiel die Fehlermeldung zu einem misslungenen Update. Für diese nichtpersistenten Daten - sie werden im folgenden als UI Zustand bezeichnet - muss ein Ablageort gefunden werden, damit sie einen Redirect überdauern. Diese Strategien bieten sich an:
Vielleicht kombiniert man diese Variante mit den Vorteilen der Speicher basierten Lösung, indem regelmäßig die ältesten Einträge eines LRU Caches nicht verworfen, sondern in eine Datenbank geschrieben werden.
Für alle Lösungen, bei denen der UI Zustand nicht zum Client geschickt werden kann, muss der UI Zustand nach einem Redirect dem folgenden GET Request zugeordnet werden können. Dafür wird eine UID, ein Token benötigt. Aus Gründen, die noch beleuchtet werden, wird diese UID als hidden Field schon in der HTML-Form hinterlegt, die den POST Request auslöst. Dieses Token wird als Parameter an die URL des Redirects angehängt und erlaubt dann die Zuweisung des UI Zustands.
Double Submit bezeichnet in Webanwendungen das ungewollte, mehrfache Versenden eines POST-Requests. Ursachen sind:
Wie bereits erwähnt verhindert das PRG Pattern ein Double Submit für die Ursachen 1 und 2. Für 3 muss jedoch eine Lösung entwickelt werden, die am besten gleich Bestandteil der PRG Pattern Implementierung wird. Eine Lösung besteht darin, ein eindeutiges Token schon in der HTML-Form anzulegen: damit lassen sich Double Submits identifizieren und verhindern. Die Logik für den Ablauf beim PRG Pattern und die Buchhaltung der UI Zustände muss an zentraler Stelle implementiert sein.
Wird für die Umsetzung der Anwendungsfälle ohnehin eine Session benötigt, dann ist hier der richtige Ort für die Buchhaltung der UIStates. Zwar kann auf synchronisierten Code nicht verzichtet werden, die Synchronisierung erfolgt aber jeweils an Session-spezifischen Objektinstanzen und ist deshalb unkritisch. Wenn keine Session zur Verfügung steht, kann man auch die Buchhaltung der UIStates an eine Filterimplementierung delegieren.
Clientseitig kann unterstützend mit JavaScript der Submit-Button ausgeschaltet werden, das ist gleichzeitig auch gutes User Interface Design, da der Nutzer seine Anfrage als "abgeschickt" identifizieren kann. Im Beispiel wird der Submit Button vor dem Submit disabled:
<input type='submit' name='Los!' onclick='this.disabled=true;this.form.submit();'>Allerdings kann man sich niemals darauf verlassen, dass clientseitig JavaScript erlaubt ist!
PRG-Pattern und das Model-2 Architekturpattern
Aus dem Bedürfnis heraus, in einer Webanwendung Code für die Darstellung von Seiten, die Manipulation von Inhalten und die steuernde Logik dafür zu separieren, entstand das Model-2 Architekturparadigma. Hier wird jeder Request von einem Controller-Servlet empfangen. Dieses Servlet leitet den Aufruf an die dafür vorgesehene Verarbeitungslogik weiter und, je nach Ergebnis der Verarbeitung, verweist an ein View, der das Ergebnis der Abarbeitung repräsentiert:
GET, POST, READ, DELETE, PUT WRITE Request --- 1 ---> Controller --- 2 ---> Verarbeitung (Servlet) | UPDATE,DELETE, | V SELECT,INSERT 4 Forward Model --- 3 ---> Backend | A V |Response <--- 5 --- View (JSP) ------------+Diese Architektur kann mit dem PRG-Pattern sogar wesentlich differenzierter umgesetzt werden. Dabei wird zunächst die Tatsache ausgenutzt, dass jeder View mit einem GET Request geholt wird:
GET READ SELECT Request -------> View ----> Model ------> Backend (JSP) | Response <--------+Alternativ dazu kann auch ein GET Request mit einem Redirect antworten. Das ist zum Beispiel der Fall, wenn die URL-Parameter des Requests nicht valide sind. POST/PUT/DELETE Requests werden grundsätzlich mit einem Redirect erwidert:
POST WRITE UPDATE Request ----> Verarbeitung ----> Model -----> Backend (Servlet) | Redirect <----------+
PUT WRITE INSERT Request ---> Verarbeitung ----> Model -----> Backend (Servlet) | Redirect <---------+
DELETE WRITE DELETE Request ------> Verarbeitung ----> Model -----> Backend (Servlet) | Redirect <-----------+Im Vergleich zum klassischen Model-2 Ansatz erfolgt der Forward demnach nicht intern sondern wird als Redirect über den Client abgewickelt. Die Abarbeitung von GET-Requests auf Views und von POST/PUT/DELETE Requests ist damit komplett separiert!
Enthält eine View Parameter die validiert werden müssen, so kann das PRG-Pattern auch hier zu einer Entkopplung von Logik (zur Validierung der Parameter) und der eigentlichen View beitragen. Dabei erfolgt zunächst ein GET Request einer HTML Form zur Validierung der Parameter. Die Verarbeitungslogik hinterlegt die validierten Parameter als UI State und endet mit einem Redirect auf die eigentliche View.
GET Request ------> Validierung ----> Model (Servlet) | Redirect <-----------+Durch das PRG-Pattern steigt dann der Grad der Wiederverwendung der Views enorm. Denn Views können nun ohne jede Fachlogik gestaltet werden, sie müssen lediglich mit einem assoziierten UI State designed werden. Im Rahmen der JSP Technologie wird man das zum Beispiel in Form von Beans machen, die mittels jsp:useBean in der JSP leicht verfügbar gemacht werden können.
Das PRG Pattern lässt sich verkürzt formulieren als:
Pfiffigerweise bringt man dieses Token schon in der HTML-Form als hidden field unter und verwendet es zur Behandlung des Double Submit Problems verursacht durch mehrfaches Drücken des Submit Knopfes der Form. Werden diese Token und die damit assoziierten UI-States vorgehalten, so kann der Benutzer problemlos auch über Seiten mit HTML-Form navigieren. Denn jeder View ist nun dank seines ergänzenden UI-States in sich konsistent und abgeschlossen.
Hypertext Transfer Protocol -- HTTP/1.1
Ausführlicher Artikel auf theserverside: Redirect After Post
seobility Wiki
PRG Pattern in JSF1.x basierend auf PhaseListener
PRG Pattern mit JSF2