Я создал свой собственный вид состояния объекта для MVC. Хорошая или слабая практика? - PullRequest
2 голосов
/ 11 июля 2009

Хорошо, я признаю это - я написал свое собственное средство для просмотра состояния 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;
    }

1 Ответ

1 голос
/ 13 июля 2009

Вопрос разумный.

Веб-приложениям потребуется хранить данные между запросами, которые связаны либо с пользователем, либо с конкретным запросом. Типичные механизмы - скрытые значения форм, состояние на стороне сервера и файлы cookie - имеют свои преимущества и недостатки.

При хранении информации, относящейся к данному запросу, я склоняюсь к использованию скрытых значений формы по умолчанию, поскольку он обеспечивает лучшую масштабируемость (нет хранилища информации на стороне сервера). Недостатком является, конечно, то, что страница может стать раздутой, если вы не будете точно знать, сколько информации вы храните. Вы также должны убедиться в том, что опубликованные данные действительны, так как они могут быть подделаны плохими парнями (цифровые подписи и шифрование являются разумными решениями).

Так что мне кажется, ваше решение вполне разумно. В прошлом я делал подобные вещи (с моим примером Dynamic Data for MVC), даже зашел так далеко, что создал собственный механизм связывания моделей, который позволил мне получить доступ к десериализованному объекту непосредственно в моих методах действий (что привело к их модульному тестированию проще, поскольку они не полагались на зашифрованные данные в полях формы).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...