Хорошо, я признаю это - я написал свое собственное средство для просмотра состояния ASP.NET MVC. Я заинтересован в критике других, особенно учитывая все искажения состояния просмотра, связанные с WebForms. С другой стороны, в Pro ASP.NET MVC Framework (p405-406) Стивен Сандерсон говорит: «Я чувствую, что как общий шаблон веб-дизайна, [ViewState] полностью здоров: веб-разработчики всегда сохраняют данные в скрытых полях формы; это просто выводит его на следующий уровень, формализуя эту технику и предоставляя аккуратный уровень абстракции ". Учитывая мою конкретную проблему, казалось, что разумно создать такой легкий слой абстракции, сохранив сильные стороны MVC: прозрачность и тестируемость.
В форме вопроса:
- Является ли использование ViewData лучшим или хотя бы надежным способом решения моей проблемы?
- Есть ли серьезные недостатки (например, производительность, безопасность) в моем конкретном подходе?
- Насколько подход вписывается в эстетику дизайна MVC?
- Есть ли лучшее решение? Если так, что это и почему?
Я пишу безопасный интерфейс для администрирования пользователей / ролей / учетных записей - такого рода вещи. Данные, извлеченные из базы данных, имеют идентификационный токен и временную метку, используемые для оптимистичного контроля параллелизма. Для таких операций, как редактирование, идентификация и временная метка должны быть связаны с клиентской операцией, которая требует некоторого рода сохранения на стороне клиента. Временная метка является ключевым драйвером этой персистентности на стороне клиента, поскольку обновление записи требует сверки временной метки поиска с текущей меткой времени, чтобы увидеть, обновил ли ее другой пользователь с момента ее первоначального извлечения. Целостность отметки времени должна сохраняться, поскольку злонамеренный пользователь может перезаписывать записи базы данных, манипулируя ею.
Обычные параметры сохранения: ViewData, TempData и состояние сеанса. Я серьезно не рассматривал другие варианты, такие как написание собственной базы данных. Я выбрал ViewData, поскольку данные могут храниться более одного раза (например, состояние сохраняется, даже если клиент переходит на другую страницу и обратно), и потому что я хотел избежать много управления данными сеанса. Я думаю, что этот подход будет довольно низким и безопасным, если только выбранные данные хранятся в ViewData и если они защищены кодом HMAC (аутентификация сообщения с хэш-кодом).
На практике я использую пару функций Encode / Decode для сериализации данных и вычисления кода HMAC, а также вспомогательный Html Html.FormState () для хранения сериализованных данных в форме. (API кодирования / декодирования немного сложнее, чем я показываю, позволяя мне хранить несколько объектов и т. Д.) Я также передаю состояние обратно в метод действия в качестве аргумента. Это поддерживает дизайн с функциональным вкусом и тем самым способствует тестируемости. Вот пример (встроенное назначение ViewData только для иллюстрации):
[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;
}