Comments 12
Сколько раз я сталкивался с проблемой доступа к отдельным свойствам модели, столько раз я видел решение на основе отображения полей.
А теперь несколько простых вопросов:
Сколько у вас в среднем получилось вью на модель?
А теперь несколько простых вопросов:
- Почему не учитываются права при Bindинге? — Мало не нарисовать контрол, нужно еще игнорировать данные, которые идут обратно.
- При платформенном биндинге будут срабатывать валидаторы, если модель биндится частично, то валидация превращается в нечто сверхсложное.
- Связаны ли отображение, биндинг и валидация? Или нужно поддерживать синхронное состояние этих частей в нескольких местах?
- Как проверять видимость/доступность полей исходя не только из прав, а например из этапа workflow?
Сколько у вас в среднем получилось вью на модель?
Да частичный биндинг не реализован, и поля, которые идут обратно не отбрасываются, и даже сохраняются в базу, все это конечно не правильно, и создает проблемы безопасности. Почему так? недостаток опыта и желание сделать побыстрее.
Валидация в основном клиентская и зависит в основном от типа полей, в тех местах, где валидация зависит от workflow приходится поддерживать в нескольких частях приложения.
Что касается четвертого пункта, то этап workflow на котором поле доступно для редактирования задается как раз аттрибутами, применение этих правил к конкретному этапу workflow идет в модели, в данном случае в базовой модели.
Спасибо за то, что указали на явные недостатки. Может быть, вы знакомы, с каким либо open source проектом, в котором все перечисленное красиво решено?
Валидация в основном клиентская и зависит в основном от типа полей, в тех местах, где валидация зависит от workflow приходится поддерживать в нескольких частях приложения.
Что касается четвертого пункта, то этап workflow на котором поле доступно для редактирования задается как раз аттрибутами, применение этих правил к конкретному этапу workflow идет в модели, в данном случае в базовой модели.
Спасибо за то, что указали на явные недостатки. Может быть, вы знакомы, с каким либо open source проектом, в котором все перечисленное красиво решено?
Валидация в основном клиентскаят.е. по факту ее нет, т.к. на сервере все проверки должны быть продублированы.
Признаюсь честно, думали о том что бы дублировать. Но наверное есть решение и получше, поэтому пока проект остается без серверной валидации.
Вот это уже звучит хреново совсем. Серверная валидация обязана присутствовать, в противном случае любой чуть знакомый с http поломает вам все, что только можно поломать.
Попробую кратко описать наш подход. Во-первых всегда нужно держать в голове полную цепочку:
На этапах 2, 4, 5 и 6 форма и правила обработки должны быть одинаковые.
Во-вторых нельзя модель данных делать параметром, который будет биндится — огромный источник ошибок как раскладки данных так и валидации, нельзя делать валидацию, встроенную в модель данных.
Далее нужно разделить простейшую валидацию (формат даты, чисел, неотключаемая обязательность и т.п.) от комплексной валидации (когда правильность значения полей зависят друг от друга или иных внешних условий).
В итоге все должно опираться на единую точку, где сосредоточены все правила, условия и настройки т.к. разработчики могут «забыть» поправить в нескольких местах.
Такой единой точкой у нас сделана одна модель — коллекция элементов (назову ее FormModel, коротко — FM), которые представляют собой Expression к модели данных + атрибуты (наименование, параметры отрисовки и т.п.). Строится эта коллекция на основе информации в ModelMetadata и далее может быть модифицирована исходя из прав, WF или данных в моделе. Так же можно эту коллекцию собрать полностью руками.
Для этой модели есть один базовый шаблон, который понимает как разместить элементы коллекции (применяется размещение в сетке с соответствующими параметрами) + шаблоны на типы свойств (строка, число, дата, файл, HTML, enum и т.п.) — всего таких шаблонов чуть более 10. В особо тяжелых случаях можно нарисовать кастомный шаблон для этой коллекции, остальная кухня (видимость полей и т.п.) остается за пределами отрисовки HTML.
Есть несколько сценариев работы, применение конкретного сценария зависит от сложности задачи.
Первый — самый простой:
Посложнее:
На втором шаге FM получаем из фабрики, при этом там уже задана функция комплексной проверки, остальное аналогично.
Самый сложный:
На втором шаге полностью готовую FM получаем из WF, который смотрит на модель данных и ее состояние + пользователя, который хочет что-то сделать.
Для сохранения на шаге 11 опять же передаем обработанную bindингом модель WF для решения, что же с ней делать.
Примерный код контроллера в простейшем случае:
В сложных случаях метод _getFM(… параметры...) выносится в фабрику или в WF.
В ходе разработки было сказано очень много «тёплых» и «ласковых» слов в адрес разработчиков платформенных ModelMetadata и Bind — пришлось искать обходные пути для нормальной работы.
Как результат: для over 2000 классов моделей данных и 3000+ действий контроллеров сделано всего чуть более 10 базовых шаблонов и пара десятков кастомных. + хитрые пользователи не смогут сохранить то, что запрещено сохранять + контроллеры просты, основная логика в сервисах и WF, view не перегружены.
Так же такая схема позволила добавлять не только элементы — свойства модели, но и статику (просто HTML) и ActionResult в единое отображение.
- Пришел запрос на форму
- Сформировалась форма с учетом прав и WF и ушла пользователю
- Пользователь что-то сделал с формой (он может сделать что угодно — браузеры позволяют и поля добавить и скрипты отключить) и послал данные на сохранение
- Данные раскладываются с учетом прав и WF
- Происходит валидация и если все плохо, то нужно просто вернуть отрисованную форму обратно с сообщениями о проблемах
- Сохраняем модель и если все плохо, то нужно просто вернуть отрисованную форму обратно с сообщениями о проблемах
- Перенаправить пользователя на страницу исходя из сценария работы
На этапах 2, 4, 5 и 6 форма и правила обработки должны быть одинаковые.
Во-вторых нельзя модель данных делать параметром, который будет биндится — огромный источник ошибок как раскладки данных так и валидации, нельзя делать валидацию, встроенную в модель данных.
Далее нужно разделить простейшую валидацию (формат даты, чисел, неотключаемая обязательность и т.п.) от комплексной валидации (когда правильность значения полей зависят друг от друга или иных внешних условий).
В итоге все должно опираться на единую точку, где сосредоточены все правила, условия и настройки т.к. разработчики могут «забыть» поправить в нескольких местах.
Такой единой точкой у нас сделана одна модель — коллекция элементов (назову ее FormModel, коротко — FM), которые представляют собой Expression к модели данных + атрибуты (наименование, параметры отрисовки и т.п.). Строится эта коллекция на основе информации в ModelMetadata и далее может быть модифицирована исходя из прав, WF или данных в моделе. Так же можно эту коллекцию собрать полностью руками.
Для этой модели есть один базовый шаблон, который понимает как разместить элементы коллекции (применяется размещение в сетке с соответствующими параметрами) + шаблоны на типы свойств (строка, число, дата, файл, HTML, enum и т.п.) — всего таких шаблонов чуть более 10. В особо тяжелых случаях можно нарисовать кастомный шаблон для этой коллекции, остальная кухня (видимость полей и т.п.) остается за пределами отрисовки HTML.
Есть несколько сценариев работы, применение конкретного сценария зависит от сложности задачи.
Первый — самый простой:
- В действии контроллера по его параметрам получаем модель данных
- Строим FM по ModelMetadata (элементы с видимостью, обязательностью, расположением и т.п. + кнопки, их может быть несколько)
- Возвращаем отрисованный стандартной view FM
- В действии сохранения контроллера по его параметрам получаем модель данных
- Строим FM по ModelMetadata
- Строим список полей, которые можно bindить
- Выполняем платформенный биндинг
- Есть ошибки? — добавляем сообщения и возвращаем отрисованный стандартной view FM
- Выполняем комплексную валидацию (у нас есть все данные — часть пришла при зачитке, часть из биндинга, но модель получилась полная)
- Есть ошибки? — добавляем сообщения и возвращаем отрисованный стандартной view FM
- Сохраняем модель данных
- Есть ошибки? — добавляем сообщения и возвращаем отрисованный стандартной view FM
- Редирект
Посложнее:
На втором шаге FM получаем из фабрики, при этом там уже задана функция комплексной проверки, остальное аналогично.
Самый сложный:
На втором шаге полностью готовую FM получаем из WF, который смотрит на модель данных и ее состояние + пользователя, который хочет что-то сделать.
Для сохранения на шаге 11 опять же передаем обработанную bindингом модель WF для решения, что же с ней делать.
Примерный код контроллера в простейшем случае:
private _dmf - Фабрика моделей данных, получаем из IoC
[HttpGet]
public ActionResult Default(...параметры...)
{
return StandardView(_getFM(...параметры...));
}
[HttpPost]
public ActionResult Default(...параметры.., FormCollection form)
{
var fm = _getFM(...параметры...);
if (fm.Bind(ControllerContext, form)) // разложит данные, разложит ошибки по элементам, добавит сообщения
{
if(_dmf.TryPut(..., fm.Model, fm.Messages) // попытка сохранить, если есть ошибки, то будут положены в fm.Messages
{
return RedirectToAction(...); // Если все Ок
}
}
return StandardView(fm); // Все плохо - выводим все, что есть и смогли разложить
}
private FormModel<TDataModel> _getFM(...параметры...)
{
var dm = _dmf.Get(User, ...параметры...);
var fm = new FormModel<TDataModel>();
fm.Model = dm;
fm.Validate = () => {....}; // <-- комплексная проверка dm
fm.Buttons.Add(...);
// Тут можно изменить элементы fm.Elements[..] - скрыть, показать, проставить обязательность, изменить расположение и т.п.
return fm;
}
В сложных случаях метод _getFM(… параметры...) выносится в фабрику или в WF.
В ходе разработки было сказано очень много «тёплых» и «ласковых» слов в адрес разработчиков платформенных ModelMetadata и Bind — пришлось искать обходные пути для нормальной работы.
Как результат: для over 2000 классов моделей данных и 3000+ действий контроллеров сделано всего чуть более 10 базовых шаблонов и пара десятков кастомных. + хитрые пользователи не смогут сохранить то, что запрещено сохранять + контроллеры просты, основная логика в сервисах и WF, view не перегружены.
Так же такая схема позволила добавлять не только элементы — свойства модели, но и статику (просто HTML) и ActionResult в единое отображение.
Сейчас не могу проверить работает ли это (вроде должно), но вместо вот этой жести
напишите
result = MvcHtmlString.Create(result.ToString().Replace("/>", "readonly = \"readonly\" />"));
напишите
result = html.EditorFor(expression,new { htmlAttributes = new { @readonly = "readonly" }});
Спасибо, поменял в статье, в моем случае рабочим вариантом оказался
html.TextBoxFor(expression, new { @readonly = "readonly" });
Погодите, погодите. Мы не может привязываться к textbox, а вдруг у нас radiobutton или checkbox или area. Нужен editor. Плюс мы еще забыли про htmlAttributes, которые идут параметром в функцию, если юзер их указывал в разметке, а мы на них забили? например
надо что-то вроде:
у меня работает
@Html.RegistratorEditorFor(model => model.Department, new { @class = "form-control" } )
надо что-то вроде:
var member = (expression.Body as MemberExpression).Member;
if (html.ViewData.Model is BaseDocumentModel)
{
if (IsReadOnly(member as PropertyInfo, html.ViewData.Model as BaseDocumentModel))
{
if (htmlAttributes == null)
htmlAttributes = new Dictionary<string, object>();
htmlAttributes.Add("readonly","readonly");
return html.EditorFor(expression,new { htmlAttributes = htmlAttributes });
}
}
return html.EditorFor(expression,htmlAttributes);
у меня работает
Вам надо познакомиться с паттерном проектирования — «Авторизация на основе ролей» (RBAC). Вы просто неверно реализуете систему разграничения прав доступа — отсюда все ваши проблемы с «говнокодом».
Ну и, конечно, валидация на сервере должна быть.
Ну и, конечно, валидация на сервере должна быть.
Sign up to leave a comment.
Попытка сделать логичное поведение форм редактирования в asp mvc