Kolumne: Dino talks

Das Post-Redirect-Get-Pattern
Kommentare

Wenn der Browser scheinbar nicht mehr reagiert oder wenn Sie auf sehr ungeduldige Benutzer stoßen, kann es sein, dass die letzte Browseroperation ständig wiederholt wird. Handelt es sich um ein GET, kann nichts weiter passieren – der Inhalt wird erneut heruntergeladen oder aus dem Cache gelesen und das Browserfenster aktualisiert. Ist dagegen die letzte Operation ein POST, werden die geposteten Daten erneut gesendet, sodass potenziell inkonsistente Ergebnisse entstehen und die Datenintegrität verletzt wird. Um das zu vermeiden, zeigt der Browser ein berüchtigtes Warnfenster an, das viele Entwickler seit Jahren auszutricksen versuchen. Wirksame Abhilfe schafft hier das Post-Redirect-Get-Pattern. Dieser Artikel zeigt, wie es sich in ASP.NET MVC einrichten lässt.

Viele Webanwendungen drehen sich um das Pattern Select Edit Save (SES). Diese Anwendungen erlauben dem Benutzer, ein Datenelement aus einer Liste auszuwählen (Select) und versetzen dann das Datenelement in den Bearbeitungsmodus (Edit). Der Benutzer kann nun den Inhalt ändern und die Anwendung schreibt die Änderungen zurück in die Speicherschicht (Save). Das bedeutet möglicherweise drei getrennte Anfragen. Zunächst wird ein GET erforderlich sein, um zur Seite zu gelangen und die Liste der Datenelemente zu empfangen. Als Nächstes ist ein POST (oder vielleicht auch ein GET) gefragt, um die Auswahl zu bestätigen und den ausgewählten Datensatz im Bearbeitungsmodus anzuzeigen. Schließlich brauchen Sie ein weiteres POST, um die Änderungen zurückzuspeichern.

In Webformularen läuft die Implementierung eines derartigen Modells aufgrund der Reichhaltigkeit von datengebundenen Steuerelementen und deklarativen Datenquellenkomponenten fast routinemäßig ab. Allerdings ist in Webformularen auch nahezu jede beliebige Operation ein POST. Dabei handelt es sich um eine leistungsfähige Operation, die Daten zur Backend-Verarbeitung auf den Server hochlädt. Aber aus eben diesem Grund ist das Wiederholen einer POST-Operation potenziell schädlich. Deshalb zeigen Browser das in Abbildung 1 dargestellte Fenster jedes Mal an, wenn ein Benutzer die letzte (POST-)Operation durch Drücken von F5 oder Wahl des Menübefehls AKTUALISIEREN wiederholt. In Webformularen gibt es keine einfache Möglichkeit, die Wiederholung von POST-Anweisungen abzuwehren. Gleichzeitig mutet die Implementierung eines alternativen Patterns wie Post Redirect Get (PRG) wegen der HTTP-Umleitungen etwas umständlich an. In ASP.NET MVC dagegen fügt sich das PRG-Pattern perfekt ein und ist zudem ideal geeignet, um URL und Inhalt der Seite vollkommen synchron zu halten.

Abb. 1: Die klassische Warnung, die Browser anzeigen, wenn ein Benutzer versucht, ein POST zu wiederholen
Abb. 1: Die klassische Warnung, die Browser anzeigen, wenn ein Benutzer versucht, ein POST zu wiederholen
Ein Datenelement aus einer Liste auswählen

Folgendes Codebeispiel zeigt die Aktion des Controllers, die eine Drop-down-Liste füllt, um dem Benutzer den ersten Bildschirm zu präsentieren. Eine interne service-Komponente ruft die Daten ab, und mithilfe eines Ansichtsmodellobjekts werden die Daten der Ansicht bereitgestellt:

public ActionResult List() { // Get the data to populate the list of customers. var list = _service.GetCustomerListItems(); // Prepare the view model var data = new CustomerIndexViewModel(); data.Customers = list; return View(data); }

Die Ansichtsvorlage erstellt die Drop-down-Liste von auswählbaren Kunden mithilfe der integrierten Html-Hilfsklasse und hüllt die Liste in ein HTML-Formular ein (Listing 1).

<% Html.BeginForm("Edit", "Customer"); %>
     <%= Html.DropDownList("ddCustomers",
               new SelectList(ViewData.Model.Customers, 
                   "CustomerId", "CompanyName") %> 
      
<% Html.EndForm(); %>  

Listing 1

Nachdem der Benutzer einen Kunden aus der Liste ausgewählt hat, klickt er auf die SUBMIT-Schaltfläche und löst damit eine POST-Anforderung für eine Edit-Aktion zur CustomerController-Klasse aus. Die Edit-Methode übernimmt die ID des geposteten Kunden mithilfe des standardmäßigen Bindungsalgorithmus übereinstimmender Feldnamen:

public ActionResult Edit(String ddCustomers) { // Parameter name match the name of a posted field : // Render the selected customer in edit mode : }

Diese Methode funktioniert gut, solange die Anforderung über ein POST geschieht. Und derartige Anforderungen erfolgen normalerweise vorwiegend über ein POST, außer in einem besonderen Szenario.

Das Datenelement über den URL auswählen

Wenn Sie einen Kunden aus der Liste auswählen, wird die Adressleiste nicht aktualisiert, um den vollständigen Pfad zum momentan angezeigten Datensatz widerzuspiegeln. Der vollständige Pfad würde „logisch“ folgendermaßen aussehen: /customer/edit/ALFKI, wobei ALFKI die ID des ausgewählten Kunden darstellt. Wenn Sie diesen URL in die Adressleiste eintippen und klicken, lösen Sie eine Anforderung nach der Methode Edit im Customer-Controller aus. Allerdings treten hierbei zwei Probleme auf. Erstens ist eine Anforderung von der Adressleiste aus eine GET-Operation, zweitens wird in einer GET-Anforderung keine Übereinstimmung zum ddCustomers-Parameter der Methode gefunden. Damit die Anfrage klappt, weisen Sie entweder der Drop-down-Liste den id-Namen zu oder modifizieren das Standard-URL-Schema in controller/action/ddCustomers. Beide Lösungen funktionieren, doch ist keine wirklich perfekt. Am besten ist es, verbspezifische Aktionen zu definieren.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -