У MVC модель повинна обробляти валідацію?


25

Я намагаюся реконструювати веб-додаток, розроблений для використання шаблону MVC, але я не впевнений, чи слід обробляти валідацію в моделі чи ні. Наприклад, я встановлюю одну з моїх моделей на зразок цієї:

class AM_Products extends AM_Object 
{
    public function save( $new_data = array() ) 
    {
        // Save code
    }
}

Перше запитання: Тому мені цікаво, чи мій метод збереження повинен викликати функцію перевірки на $ new_data чи припустити, що дані вже перевірені?

Крім того, якби запропонувати перевірку, я думаю, що деякий з модельних кодів для визначення типів даних виглядатиме так:

class AM_Products extends AM_Object
{
    protected function init() // Called by __construct in AM_Object
    {
        // This would match up to the database column `age`
        register_property( 'age', 'Age', array( 'type' => 'int', 'min' => 10, 'max' => 30 ) ); 
    }
}

Друге запитання: Кожен дочірній клас AM_Object запускатиме register_property для кожного стовпця в базі даних конкретного об'єкта. Я не впевнений, це хороший спосіб зробити це чи ні.

Третє запитання: Якщо перевірка повинна оброблятися моделлю, чи повинна вона повертати повідомлення про помилку або код помилки та чи має перегляд використовувати код для відображення відповідного повідомлення?

Відповіді:


30

Перший відповідь: Ключова роль моделі полягає у підтримці цілісності. Однак обробка вводу користувача є відповідальністю контролера.

Тобто контролер повинен переводити дані користувача (які більшість часу - це лише рядки) у щось значуще. Це вимагає розбору (і може залежати від таких речей, як локаль, враховуючи, що, наприклад, є різні десяткові оператори тощо).
Отже, фактична перевірка, як у "чи дані добре сформовані?", Повинна виконувати контролер. Однак перевірка, як у "чи мають сенс дані?" слід виконувати в межах моделі.

Щоб уточнити це на прикладі:
Припустимо, ваша програма дозволяє вам додати деякі сутності з датою (наприклад, проблема із мертвою лінією). У вас може бути API, де дати можуть бути представлені як прості часові позначки Unix, тоді як при надходженні зі сторінки HTML це буде набір різних значень або рядок у форматі MM / DD / YYYY. Цю інформацію у моделі не потрібно. Ви хочете, щоб кожен контролер окремо намагався з'ясувати дату. Однак, коли дата переноситься на модель, модель повинна зберігати цілісність. Наприклад, може бути доцільним забороняти дати в минулому або дати, які є святами / неділями тощо.

Ваш контролер містить правила введення (обробки). Ваша модель містить правила бізнесу. Ви хочете, щоб правила вашого бізнесу завжди виконувалися, незалежно від того, що відбувається. Якщо припустити, що у контролері були ділові правила, то вам доведеться їх дублювати, якщо ви коли-небудь створили інший контролер.

Другий відповідь: Підхід має сенс, проте метод може бути більш потужним. Замість того, щоб останній параметр був масивом, він повинен бути екземпляром, IContstraintякий визначається як:

interface IConstraint {
     function test($value);//returns bool
}

А для чисел у вас може бути щось на зразок

class NumConstraint {
    var $grain;
    var $min;
    var $max;
    function __construct($grain = 1, $min = NULL, $max = NULL) {
         if ($min === NULL) $min = INT_MIN;
         if ($max === NULL) $max = INT_MAX;
         $this->min = $min;
         $this->max = $max;
         $this->grain = $grain;
    }
    function test($value) {
         return ($value % $this->grain == 0 && $value >= $min && $value <= $max);
    }
}

Також я не бачу, що 'Age'мається на увазі представляти, якщо чесно. Це фактична назва власності? Якщо припустити, що за замовчуванням існує умовна умова, параметр може просто перейти до кінця функції та бути необов'язковим. Якщо не встановлено, воно буде за замовчуванням to_camel_case імені стовпця БД.

Таким чином, приклад дзвінка виглядатиме так:

register_property('age', new NumConstraint(1, 10, 30));

Сенс використання інтерфейсів полягає в тому, що ви можете додавати все більше і більше обмежень у міру розвитку, і вони можуть бути такими ж складними, як ви хочете. Щоб рядок відповідав регулярному виразу. Щоб побачення було принаймні на 7 днів попереду. І так далі.

Третя відповідь: Кожна модель моделі повинна мати такий метод Result checkValue(string property, mixed value). Контролер повинен викликати його до встановлення даних. ResultПовинні мати всю інформацію про те , не пройшла перевірку, і в разі , якщо він зробив, дають підстави, так що контролер може поширюватися ті, по думку відповідно.
Якщо неправильне значення передається моделі, модель повинна просто відповісти, піднявши виняток.


Дякую за цей запис. Це прояснило багато речей про MVC.
AmadeusDrZaius

5

Я не повністю згоден з "back2dos": моя рекомендація завжди використовувати окремий шар форми / перевірки, який контролер може використовувати для перевірки вхідних даних, перш ніж надсилати в модель.

З теоретичної точки зору, перевірка моделі працює на довірених даних (внутрішній стан системи) і в ідеалі повинна бути повторюваною в будь-який момент часу, тоді як перевірка введення явно одноразово діє на дані, що надходять з ненадійних джерел (залежно від випадку використання та привілеїв користувача).

Такий поділ дає змогу побудувати моделі багаторазового використання, контролери та форми, які можна зв'язати з допомогою інжекцій залежностей. Подумайте про перевірку введення як валідацію білого списку («прийняти відоме хороше»), а валідацію моделі - як перевірку чорного списку («відхилити відоме погано»). Перевірка білого списку є більш безпечною, тоді як перевірка чорного списку запобігає надмірному обмеженню шару моделі в дуже конкретних випадках використання.

Неправильні дані моделі завжди повинні спричиняти викид виключень (інакше додаток може продовжувати працювати, не помічаючи помилки), тоді як недійсні вхідні значення, що надходять із зовнішніх джерел, не є несподіваними, але досить поширеними (якщо ви не маєте користувачів, які ніколи не помиляються).

Дивіться також: https://lastzero.net/2015/11/why-im-using-a-separate-layer-for-input-data-validation/


Для простоти припустимо, що існує сім'я класів Validator і що всі перевірки виконуються за стратегічною ієрархією. Конкретні валідатори можуть також складатися з спеціальних валідаторів: електронної пошти, номера телефону, жетонів форми, капчу, пароля та інших. Перевірка входів контролера має два види: 1) Перевірка існування контролера та методу / команди та 2) попередня перевірка даних (тобто метод запиту HTTP, скільки входів даних (Забагато? Занадто мало?).
Ентоні Ратлідж

Після того, як кількість введених даних буде підтверджено, вам потрібно знати, що правильні елементи керування HTML були подані за назвою, маючи на увазі, що кількість входів на запит може змінюватись, оскільки не всі елементи керування HTML-форми надсилають щось, коли воно залишається порожнім ( особливо прапорці). Після цього остання попередня перевірка - це перевірка розміру вводу. На мою думку, це має бути рано , а не пізно. Перевірка кількості, імені управління та перевірки розміру базового входу у валідаторі контролера означатиме наявність валідатора для кожної команди / методу в контролері. Я вважаю, що це робить вашу заявку більш захищеною.
Ентоні Ратлідж

Так, валідатор контролера для команди буде щільно пов'язаний з аргументами (якщо такі є), необхідними для модельного методу , але сам контролер не буде, окрім посилання на вказаний валідатор контролера . Це гідний компроміс, оскільки не слід йти вперед з припущенням, що більшість матеріалів буде законною. Чим раніше ви зможете припинити нелегітимний доступ до своєї програми, тим краще. Якщо це зробити в класі валідатора контролера (кількість, назва та максимальний розмір входів), ви позбавите вас від необхідності інстанціювати всю модель для відхилення явно шкідливих запитів HTTP.
Ентоні Рутлідж

Зважаючи на це, перед вирішенням проблем із максимальним розміром вхідних даних потрібно переконатися, що кодування добре. Зважаючи на все, це занадто багато, щоб модель могла робити, навіть якщо робота інкапсульована. Відкидати шкідливі запити стає зайвим. Підсумовуючи це, контролер повинен брати на себе більше відповідальності за те, що він надсилає моделі. Поломка рівня контролера повинна бути фатальною, без інформації про повернення запитувача, окрім 200 ОК. Реєструйте активність. Киньте фатальний виняток. Припиніть усі дії. Зупиніть всі процеси якомога швидше.
Ентоні Рутлідж

Мінімальні елементи керування, максимальні елементи керування, правильні елементи керування, кодування входу та максимальний розмір вводу стосуються характеру запиту (так чи інакше). Деякі люди не визначили ці п’ять основних речей як визначальних, чи слід задовольняти прохання. Якщо всі ці речі не задоволені, чому ви надсилаєте цю інформацію моделі? Гарне питання.
Ентоні Рутлідж

3

Так, модель повинна виконати перевірку. Користувальницький інтерфейс повинен також підтвердити введення даних.

Однозначно відповідальність моделі визначає дійсні значення та стани. Іноді такі правила часто змінюються. У такому випадку я б подала модель з метаданих та / або прикрашала її.


Як щодо випадків, коли наміри користувача явно зловмисні чи помилкові? Наприклад, певний HTTP-запит повинен мати не більше семи (7) вхідних значень, але ваш контролер отримує сімдесят (70). Ви дійсно збираєтесь дозволити десять разів (10 разів) кількість дозволених значень потрапити на модель, коли запит явно пошкоджений? У цьому випадку йдеться про стан всього запиту, а не про стан якоїсь певної цінності. Стратегія поглибленої оборони передбачає, що природу HTTP-запиту слід вивчити перед відправкою даних на модель.
Ентоні Рутлідж

(продовження) Таким чином, ви не перевіряєте, чи вказані користувачем значення та стани є дійсними, але чи є сукупність запиту дійсною. Поки що ще немає необхідності висвітлювати. Масло вже на поверхні.
Ентоні Рутлідж

(продовження) Немає способу змусити перевірити передумови. Потрібно врахувати, що за допомогою автоматизованих інструментів можна використовувати інтерфейс із вашим веб-додатком.
Ентоні Рутлідж

(Подумавши) Допустимі значення і стан даних в моделі мають важливі значення, але то , що я описав хіти на цілі запиту , що надходить через контролер. Якщо пропустити перевірку наміру, ваша програма стає більш вразливою. Намір може бути лише добрим (граючи за вашими правилами) або поганим (виходячи за свої правила). Намір можна перевірити за допомогою основних перевірок на вході: мінімальних елементів керування, максимальних елементів керування, правильних елементів керування, кодування входу та максимального розміру вводу. Це пропозиція все або нічого. Все проходить, або запит недійсний. Не потрібно нічого надсилати моделі.
Ентоні Рутлідж

2

Чудове запитання!

Що стосується всесвітньої розробки веб-сайтів, що робити, якщо ви також запитуєте наступне?

"Якщо неправильний вхід користувача надходить на контролер з користувальницького інтерфейсу, чи повинен контролер оновлювати Вигляд у вигляді циклічного циклу, змушуючи команди та вхідні дані бути точними перед їх обробкою ? Як? Як огляд оновлюється у звичайному режимі умови? Перегляд щільно пов'язаний з моделлю? Чи є коректна логіка введення користувачем основної ділової логіки моделі, чи вона є попередньою для неї і, таким чином, повинна відбуватися всередині контролера (тому що вхідні дані користувача є частиною запиту)?

(Насправді, чи може, чи потрібно, одна затримка створення моделі, поки не буде отримано хороший внесок?)

На мою думку, моделі повинні керувати чистою та незайманою обставиною (наскільки це можливо), не обтяженою базовою валідизацією вхідного запиту HTTP, яка повинна відбуватися до інстанціфікації моделі (і, безумовно, до того, як модель отримає вхідні дані). Оскільки управління даними про стан (постійними або в іншому випадку) та співвідношеннями API є світом моделі, нехай у контролері відбувається перевірка основного введення запиту HTTP .

Підбиття підсумків.

1) Підтвердьте свій маршрут (проаналізований за URL-адресою), оскільки контролер та метод повинні існувати, перш ніж все інше може йти вперед. Це неодмінно має відбутися в царині переднього контролера (клас маршрутизатора), перш ніж перейти до справжнього контролера. Дух. :-)

2) У моделі може бути багато джерел вхідних даних: HTTP-запит, база даних, файл, API та так, мережа. Якщо ви збираєтеся розмістити всю модель перевірки вхідних даних у моделі, тоді ви вважаєте перевірку введення запиту HTTP частиною бізнес-вимог до програми. Справа закрита.

3) Тим не менш, короткозоріше переживати за рахунок екземплярів безлічі об'єктів, якщо вхід HTTP-запиту не є корисним! Ви можете знати, чи добре ** введення запиту HTTP ** ( що надійшло із запитом ), перевіривши його перед тим, як ініціювати модель та всі її складності (так, можливо, ще більше валідаторів для вхідних / вихідних даних API та БД).

Перевірте наступне:

a) Метод запиту HTTP (GET, POST, PUT, PATCH, DELETE ...)

б) Мінімальні елементи керування HTML (чи вистачає?).

в) Максимальне керування HTML (у вас занадто багато?).

г) Правильні елементи керування HTML (у вас є правильні?).

e) Вхідне кодування (як правило, це кодування UTF-8?).

f) Максимальний розмір введення (чи будь-який вхід дико виходить за межі?).

Пам'ятайте, що ви можете отримати рядки та файли, тому очікування моделі інстанції може стати дуже дорогою, оскільки запити потрапляють на ваш сервер.

Те, що я описав тут, потрапляє під намір запиту, що надходить через контролер. Якщо пропустити перевірку наміру, ваша програма стає більш вразливою. Намір може бути лише добрим (граючи за вашими основними правилами) або поганим (виходячи за рамки ваших основних правил).

Намір для HTTP-запиту - це пропозиція, яка повністю або нічого. Все проходить, або запит недійсний . Не потрібно нічого надсилати моделі.

Цей базовий рівень наміру HTTP-запиту не має нічого спільного з регулярними помилками введення користувача та валідацією. У моїх програмах HTTP-запит повинен бути дійсним п’ятьма способами, щоб я його шанував. При поглибленому захисному мовленні ви ніколи не переходите до перевірки введення користувачем на стороні сервера, якщо будь-які ці п'ять речей виходять з ладу.

Так, це означає, що навіть введення файлу повинно відповідати вашим спробам перевірити та повідомити користувачеві максимальний розмір файлу, що приймається. Тільки HTML? Немає JavaScript? Добре, але користувач повинен бути поінформований про наслідки завантаження файлів, які занадто великі (головним чином, що вони втратять усі дані форми та будуть викинуті з системи).

4) Чи означає це, що вхідні дані HTTP-запиту не є частиною бізнес-логіки програми? Ні, це просто означає, що комп'ютери - це кінцеві пристрої, і ресурси потрібно використовувати розумно. Є сенс припинити шкідливу діяльність швидше, не пізніше. Ви платите більше за обчислювальні ресурси за очікування, щоб зупинити його пізніше.

5) Якщо вхід HTTP-запиту поганий, весь запит поганий . Ось як я на це дивлюся. Визначення хорошого введення запиту HTTP випливає з бізнес-вимог моделі, але має бути певна точка розмежування ресурсів. Скільки часу ви будете пускати поганий запит, перш ніж вбити його і сказати: "Ой, ей, неважливо. Погане прохання".

Судження полягає не лише в тому, що користувач допустив помилкову помилку введення, але і те, що HTTP-запит настільки поза межами, що його потрібно оголосити шкідливим і негайно зупинити.

6) Отже, на мої гроші запит HTTP (МЕТОД, URL / маршрут та дані) або ВСЕ добре, або НІЧОГО іншого не може продовжуватися. Надійна модель вже має завдання перевірки, що стосується себе, але хороший пастух-ресурс каже: "Мій шлях, або високий шлях. Приходьте правильно, або не приходьте зовсім".

Однак це ваша програма. "Існує більше ніж один спосіб зробити це". Деякі способи коштують дорожче часу та грошей, ніж інші. Перевірка даних HTTP-запиту пізніше (у моделі) повинна коштувати дорожче впродовж життя програми (особливо якщо масштабування чи зменшення).

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

Деякі вважають це абсолютно невірним головою, але HTTP був у зародковому стані, коли Банда з чотирьох написала шаблони дизайну: елементи повторно використовуваного об’єктно-орієнтованого програмного забезпечення .

===================================================== =========================

Тепер, як це стосується звичайної перевірки введення користувача (після того, як HTTP-запит вважається дійсним), він оновлює представлення даних, коли користувач заблукає, що вам потрібно думати! Такий вид перевірки вводу користувача повинен відбуватися в моделі.

У вас немає гарантії JavaScript на передній панелі. Це означає, що ви не можете гарантувати асинхронне оновлення інтерфейсу програми зі статусом помилок. Справжнє прогресивне вдосконалення також охоплюватиме випадки синхронного використання.

Облік випадку синхронного використання - це мистецтво, яке втрачається все більше і більше, оскільки деякі люди не хочуть проходити час і клопоту відслідковувати стан усіх своїх хитрощів у користувальницькому інтерфейсі (показати / приховати елементи керування, відключити / увімкнути елементи керування , вказівки про помилки, повідомлення про помилки) на задньому плані (як правило, шляхом відстеження стану в масивах).

Оновлення : На діаграмі я кажу, що Viewслід посилатися на Model. Ні. Вам слід передати дані в Viewзвідти, Modelщоб зберегти нещільне з'єднання. введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.