2 Stimmen

Ich habe meine eigene Ansicht Zustand Anlage für MVC erstellt. Gute oder schwache Praxis?

Ok, ich gebe es zu - ich schrieb meine eigene Ansicht Zustand Anlage für ASP.NET MVC. Ich bin an der Kritik anderer interessiert, vor allem angesichts des ganzen View-State-Bashings im Zusammenhang mit WebForms. Auf der anderen Seite, in Pro ASP.NET MVC-Framework (S. 405-406) Steven Sanderson sagt: "Ich glaube, dass [ViewState] als allgemeines Web-Design-Muster völlig in Ordnung ist: Webentwickler haben schon immer Daten in versteckten Formularfeldern aufbewahrt; dies bringt es einfach auf die nächste Stufe, indem es diese Technik formalisiert und eine saubere Abstraktionsschicht bietet." In Anbetracht meines spezifischen Problems schien es ein vernünftiger Ansatz zu sein, eine solche leichtgewichtige Abstraktionsschicht zu schaffen und gleichzeitig die Stärken von MVC, nämlich Transparenz und Testbarkeit, beizubehalten.

In Frageform:

  • Ist die Verwendung von ViewData die beste oder zumindest eine starke Möglichkeit, mein Problem zu lösen?
  • Gibt es gravierende Schwachstellen (z. B. Leistung, Sicherheit) in meinem spezifischen Ansatz?
  • Wie gut passt der Ansatz zur MVC-Designästhetik?
  • Gibt es eine bessere Lösung? Wenn ja, wie lautet sie und warum?

Ich schreibe eine sichere Schnittstelle zur Verwaltung von Benutzern/Rollen/Konten - so etwas in der Art. Die aus der Datenbank abgerufenen Daten haben ein Identitäts-Token und einen Zeitstempel, die für die optimistische Gleichzeitigkeitskontrolle verwendet werden. Für Operationen wie die Bearbeitung müssen die Identität und der Zeitstempel mit der Client-Operation verknüpft werden, was eine Art clientseitige Persistenz voraussetzt. Der Zeitstempel ist ein Schlüsselfaktor für diese clientseitige Persistenz, da bei der Aktualisierung eines Datensatzes der Zeitstempel des Abrufs mit dem aktuellen Zeitstempel verglichen werden muss, um festzustellen, ob ein anderer Benutzer den Datensatz seit dem ursprünglichen Abruf aktualisiert hat. Die Integrität des Zeitstempels muss gewahrt bleiben, da ein böswilliger Benutzer durch Manipulation des Zeitstempels Datenbankeinträge überschreiben könnte.

Die üblichen Persistenzoptionen sind ViewData, TempData und Sitzungsstatus. Andere Optionen wie das Schreiben einer eigenen Datenbankeinrichtung habe ich nicht ernsthaft in Betracht gezogen. Ich habe mich für ViewData entschieden, da die Daten für mehr als einen Roundtrip aufbewahrt werden können (z.B. bleibt der Status auch dann erhalten, wenn ein Client zu einer anderen Seite und zurück springt) und weil ich eine Menge Sitzungsdatenverwaltung vermeiden wollte. Ich denke, dass der Ansatz relativ wenig Aufwand erfordert und sicher ist, wenn nur ausgewählte Daten in ViewData gespeichert werden und diese mit einem HMAC-Code (Hashing Code Message Authentication) geschützt sind.

In der Praxis verwende ich ein Funktionspaar Encode/Decode, um die Daten zu serialisieren und den HMAC-Code zu berechnen, und einen Html-Helfer Html.FormState(), um die serialisierten Daten im Formular zu speichern. (Die Encode/Decode-API ist etwas komplizierter, als ich zeige, denn sie ermöglicht es mir, mehrere Objekte zu speichern usw.) Außerdem gebe ich den Status als Argument an die Aktionsmethode zurück. Dadurch wird ein Design mit funktionalem Charakter beibehalten und die Testbarkeit gefördert. Hier ist ein Beispiel (die Inline-Zuweisung zu ViewData dient nur zur Veranschaulichung):

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Edit(Guid? id) {
        User user = _crmContext.Users.GetUser(id ?? Guid.Empty);
        if (user == null) {
            TempMessage = "User not found";
            return RedirectToAction("Index");
        }
        else {
            ViewData["formState"] = EncodeState("user", user);
            return View(user);
        }
    }

    [AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken]
    public ActionResult Edit(Guid? id, string formState) {
        User user = DecodeState("user", formState) as User;
        if (user == null || id != user.UserId) {
            TempMessage = "User not found";
            return RedirectToAction("Index");
        }
        else {
            try {
                UpdateModel(user, "user");
                _crmContext.Users.UpdateUser(user);
                TempMessage = "User changes saved.";
                return RedirectToAction("Details", new { id = user.UserId });
            }
            catch (RulesException e) {
                e.AddModelStateErrors(ModelState, "user");
                ViewData["formState"] = EncodeState("user", user);
                return View(user);
            }
        }
    }

    public static string FormState(this HtmlHelper html) {
        string anti = html.AntiForgeryToken();
        string data = html.Hidden("formState");
        return "\n" + anti + "\n" + data;
    }

1voto

Brad Wilson Punkte 64944

Die Frage ist berechtigt.

Webanwendungen müssen zwischen den Anfragen Daten speichern, die entweder mit dem Benutzer oder mit der spezifischen Anfrage verbunden sind. Die typischen Mechanismen - versteckte Formularwerte, serverseitiger Status und Cookies - haben alle ihre Vor- und Nachteile.

Bei der Speicherung von Informationen, die für eine bestimmte Anfrage spezifisch sind, neige ich dazu, versteckte Formularwerte zu verwenden, da dies die beste Skalierbarkeit bietet (kein serverseitiger Informationsspeicher). Der Nachteil ist natürlich, dass die Seite aufgebläht werden kann, wenn man nicht genau darauf achtet, wie viele Informationen man speichert. Außerdem müssen Sie sicherstellen, dass die zurückgesendeten Daten gültig sind, da sie von Bösewichten manipuliert werden könnten (digitale Signaturen und Verschlüsselung sind beide sinnvolle Lösungen).

Ihre Lösung erscheint mir also durchaus vernünftig. Ich habe ähnliche Dinge in der Vergangenheit getan (mit meinem Dynamic Data für MVC-Beispiel), sogar so weit gehen, um eine benutzerdefinierte Modell-Binder zu bauen, die mir erlaubt, Zugriff auf das deserialisierte Objekt direkt in meine Aktion Methoden (die Unit-Tests sie einfacher gemacht, da sie nicht auf mit verschlüsselten Daten in Formularfeldern angewiesen waren) zu erhalten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X