Note that there are some explanatory texts on larger screens.

plurals
  1. POI created my own view state facility for MVC. Good or weak practice?
    text
    copied!<p>Ok, I admit it - I wrote my own view state facility for ASP.NET MVC. I am interested in others' critique, especially given all the view state bashing associated with WebForms. On the other hand, in <i>Pro ASP.NET MVC Framework</i> (p405-406) Steven Sanderson says "I feel that as a general web design pattern, [ViewState] is completely sound: web developers have always preserved data in hidden form fields; this just takes it to the next level by formalizing that technique and providing a neat abstraction layer." Given my specific problem, it seemed like a reasonable approach to create such a lightweight abstraction layer while retaining MVC's strengths of transparency and testability.</p> <p>In question form: </p> <ul> <li> Is using ViewData the best or at least a strong way to solve my problem? <li> Are there serious weaknesses (e.g., performance, security) in my specific approach? <li> How well does the approach fit with the MVC design esthetic? <li> Is there a better solution? If so, what is it and why? </ul> <p>I am writing a secure interface to administer users/roles/accounts - that sort of thing. Data retrieved from the database has a identity token and a timestamp used for optimistic concurrency control. For operations like editing, the identity and timestamp have to be associated with the client operation, which calls for some sort of client-side persistence. The timestamp is a key driver of this client-side persistence, since updating a record requires checking the retrieval timestamp against the current timestamp to see if another user has updated it since it was originally retrieved. The integrity of the timestamp must be perserved since a malicious user could overwrite database records by manipulating it.</p> <p>The usual persistence options are ViewData, TempData, and session state. I didn't seriously consider other options such as writing my own database facility. I chose ViewData since the data can be retained for more than a single round-trip (e.g., the state is retained even if a client jumps to another page and back) and because I wanted to avoid a lot of session data management. My thinking is that the approach will be fairly low overhead and secure, if only select data are stored in ViewData and if it is protected with a HMAC (hashing code message authentication) code.</p> <p>In practice, I use a pair of functions Encode/Decode to serialize the data and calculate the HMAC code, and an Html helper Html.FormState() to store the serialized data on the form. (The Encode/Decode API is a little more involved than I show, enabling me to store multiple objects, etc.) I also pass the state back into the action method as an argument. This maintains a design with a functional flavor and thus promotes testability. Here's a sample (the inline assignment to ViewData is just for illustration):</p> <pre><code> [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; } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload