2009-10-14 17 views
5

Ich verwende das Muster Redirect After Post in meiner ASP.NET MVC-Anwendung. Ich habe das folgende Szenario:Redirect After Post in ASP.NET MVC

  1. Benutzer geht an /controller/index wo er aufgefordert wird, ein Formular ausfüllen.
  2. Formularwerte werden an /controller/calculate POST gesendet.
  3. Die Aktion Calculate führt die Berechnung basierend auf der Eingabe durch und instanziiert ein komplexes Objekt, das die Ergebnisse der Operation enthält. Dieses Objekt wird in TempData gespeichert und Benutzer wird zu /controller/result umgeleitet.
  4. /controller/result ruft das Ergebnis von TempData ab und rendert sie dem Benutzer.

Das Problem bei diesem Ansatz ist, dass, wenn der Benutzer F5 trifft, während die Ergebnisse in /controller/result die Seite sieht nicht mehr als TempData gemacht werden kann, ist abgelaufen worden und das Ergebnis Objekt ist nicht mehr verfügbar.

Dieses Verhalten wird von den Benutzern nicht gewünscht. Eine mögliche Lösung wäre, statt nach dem POST nur die Ergebnisansicht zu rendern. Wenn der Benutzer jetzt F5 drückt, erhält er einen Browserdialog, in dem er fragt, ob er das Formular erneut senden möchte. Dies war auch nicht erwünscht.

Eine mögliche Lösung, an die ich dachte, war, das Ergebnisobjekt zu serialisieren und es in der URL vor dem Umleiten zu übergeben, aber AFAIK gibt es einige Einschränkungen in der Länge einer GET-Anfrage und wenn das Objekt ziemlich groß wird, könnte ich diese Einschränkung (besonders wenn base64 codiert ist). Eine andere Möglichkeit wäre, das Session Objekt anstelle von TempData zu verwenden, um die Ergebnisse zu erhalten. Bevor ich diese Lösung implementiere, würde ich gerne wissen, ob es einen besseren Weg gibt.


UPDATE:

Untersuchung des Weiteren die Frage, die ich, dass herausgefunden, wenn ich in TempData das Ergebnis Objekt neu setzen in der /controller/result Aktion es tatsächlich funktioniert:

public ActionResult Result() 
{ 
    var result = TempData["result"]; 
    TempData["result"] = result; 
    return View(result); 
} 

Aber das fühlt sich irgendwie von schmutzig. Könnte es bei diesem Ansatz irgendwelche Nebenwirkungen geben (z. B. Wechsel zu Out-of-Process-Sitzungsanbietern, da ich derzeit InProc verwende)?

+0

Wenn Sie Redirect sagen, rufen Sie RedirectToAction? – Will

+0

Ja, RedirectToAction. –

Antwort

5

Speichern Sie es in der Sitzung mit einigen eindeutigen Schlüssel und übergeben Sie den Schlüssel als Teil der URL. Solange die Sitzung noch läuft, können sie die Zurück-/Vorwärts-Taste nach Herzenslust verwenden und die URL immer noch richtig beantworten lassen.Alternativ können Sie den ASP-Cache verwenden, aber normalerweise reserviere ich das für Objekte, die von Benutzern gemeinsam genutzt werden. Wenn Sie die Parameter für die Berechnung als Schlüssel verwendet haben und das Ergebnis im Cache gefunden haben, können Sie es natürlich einfach erneut verwenden.

+0

Danke, das ist ein guter Vorschlag. Ich würde versuchen, es mit einem eindeutigen Schlüssel zu implementieren, der in der URL übergeben wird. –

1

TempData wird im Allgemeinen als nützlich für das Zurückgeben von Nachrichten an den Benutzer und nicht für das Speichern von Arbeitseinheiten angesehen (eine Benutzeraktualisierung wird den Inhalt von TempData aufheben).

Ich kenne keinen geeigneteren Ort als die Sitzung, um diese Art von Informationen zu speichern. Ich denke, die allgemeine Idee ist, die Sitzung so klein wie möglich zu halten. Persönlich schreibe ich normalerweise einige Wrapper, um bestimmte Objekte hinzuzufügen oder zu entfernen. Wenn möglich, manuell aufräumen.

Alternativ können Sie in einer Datenbank speichern, in der Sie veraltete Elemente regelmäßig löschen.

2

Ich denke Umleitung nach Post macht viel mehr Sinn, wenn die resultierende URL sinnvoll ist. In Ihrem Fall würde es bedeuten, dass alle für die Berechnung benötigten Daten in der URL von/controller/result stehen.

/Controller/berechnen würde nicht die Berechnung aber/Controller/Ergebnis tun.

Wenn Sie dies erledigen können, wird es ziemlich einfach: Sie hash die für die Berechnung erforderlichen Werte und verwenden sie als Schlüssel für den Cache. Wenn der Benutzer aktualisiert wird, trifft er nur den Cache.

Wenn Sie keine sinnvolle URL haben, können Sie in/controller/index posten. Wenn der Benutzer F5 schlägt, würde die Berechnung erneut beginnen, aber ein Cache mit dem Hash als Schlüssel würde wieder helfen.

+0

Ich muss die Eingabe vor der Berechnung validieren und wenn es einen Fehler gibt, zeige das gleiche Formular mit vorgefüllten Werten und Fehlerfeldern, die rot markiert sind. Wenn die Berechnung in der Aktion "Result" ausgeführt wurde, wie würde ich mit ModelState-Fehlern umgehen? Sollte ich zurück zur Index-Aktion umleiten und alle Eingabewerte und Validierungsfehler wieder auf die TempData setzen, damit ich das Formular korrekt anzeigen kann? –

+0

Es ist schwieriger, Validierung im Ergebnis zu haben. Ich würde mit validierten Werten darauf umleiten. Der einfachste Weg ist es, dort Daten zu indizieren und zu validieren. –

0

Ich könnte eine ähnliche Idee zu vielen Banken auf ihren Online-Banking-Websites annehmen, indem Sie Einmalschlüssel verwenden, um alle POSTs zu verifizieren. Sie können es beispielsweise in einen HTML-Helfer für Formulare und in Ihre Serviceebene zur Verifizierung integrieren.

Angenommen, Sie möchten nur einmal eine Instanz eines Formulars posten. Fügen Sie dem Formular eine GUID hinzu. Wenn das Formular nicht zurückgesendet wird und die Daten festgeschrieben sind, möchten Sie die GUID ungültig machen und zur GET-Aktion umleiten. Wenn das Formular nicht gültig ist, wenn die Seite zurücksendet, benötigen Sie eine neue (gültige) GUID im Formular, die auf den nächsten Postversuch wartet.

GUIDs werden nach Bedarf generiert und einer Tabelle in Ihrer Datenbank hinzugefügt. Da sie ungültig sind (durch POSTS, ob erfolgreich oder nicht), werden sie in der Tabelle markiert. Vielleicht möchten Sie die Tabelle um 100 Zeilen oder 1000 abschneiden, je nachdem, wie groß Ihre App ist und wie viele gerenderte, aber noch nicht veröffentlichte Formulare Sie zur gleichen Zeit haben.

Ich habe diesen Entwurf nicht wirklich gut abgestimmt, aber ich denke, es könnte funktionieren. Es wird nicht so stinkend sein wie die TempData und Sie können immer noch an dem PRG-Muster festhalten.

Erinnern Sie sich, mit PRG möchten Sie nicht die neuen Daten an die GET-Aktion in einer Temp-Variable einer Art senden. Sie möchten es vom Datenspeicher abfragen, für den es jetzt festgeschrieben ist.

+0

Derzeit verfügt die Anwendung über keinen Datenspeicher. Es führt alle Arbeiten im Speicher durch (nur Berechnungen und keine Persistenz). Aus diesem Grund habe ich überlegt, TempData oder Session als temporären Datenspeicher zu verwenden. –

0

Wie Michael sagte, TempData hat einen einzigen Zweck -> ein Objekt für nur eine Reise und nur eine Reise zu speichern. Wie ich es verstehe, verwendet TempData im Wesentlichen das gleiche Session-Objekt, das Sie möglicherweise verwenden, aber es wird das Objekt bei der nächsten Reise automatisch aus der Sitzung entfernen.

Halten Sie sich mit Session imho, anstatt in TempData zurück zu schieben.