— введите число
— -1.1
— положительное!
— 1.1
— целое!
— 1
— чётное!
— как же вы задолбали со своими исключениями. нельзя что-ли сразу все ошибки выдать?
— введите число
К тому же (при регистрации пользователя например) может проверяться то, что ещё не известно до проверки.
Думаю пользователь не поймёт юмора, если ему написать «Введите корректный логин, который ещё не используется»
абсолютно задолбало!
особенно когда где-нибудь регистрируешься, и логин-пароль только с десятой попытки разрешает, каждый раз находя какую-нибудь чепуху.
очень бесит!
а вы думали над реализацией механизм валидации на базе атрибутов и регулярных выражений? скажем для метода задается набор атрибутов для каждого из параметров с указанием регулярного выражения для валидации.
По-моему это не совсем то. Я не говорю о проверке параметров метода. Я говорю именно о проверке значений, введёных пользователем в некую форму и сопоставление каждой проверке некоторого сообщения, которое должно выдаваться пользователю если он ввёл неверное значение.
Основной идеей является сократить код, который в любом случае придётся писать, до минимума, чтобы с одного взгляда было понятно, что значение проверяется сначала на пустоту, потом на то, что оно является целым, а затем, что оно принадлежит некоторому интервалу.
Главное, что так можно организовать проверку любого значения на любые условия.
а у меня валидацией занимается одна функция, она принимает обьект, в котором находится регулярное выражение и текст ошибки, и данные пользователя… в итоге мы имеем одну универсальную функцию, которой надо лишь дать один обьект с 2-мя параметрами и все, кода меньше чем минимум :) как Вам такое?
Смысл в том, что если пользователь, например, не введёт логин вообще, то ему нужно выдавать не сообщение о том что «введён неправильный логин» а о том, что «Вам необходимо ввести логин» или «Вы забыли ввести логин»
В ValidationExtensions можно добавить и метод проверки регулярных выражений:
public static ValidationStepRegexValidate(this ValidationStepstep,
string regexString, string message)
{
var regex = new Regex(regexString);
if(regex.Match(step.Value)) return step;
else throw new ValidationException(message);
}
Тогда функция авторизации пользователя будет выглядеть так:
bool Auth(string login, string password)
{
bool success = true;
Validation.Validate(
login, value => value.Validate()
.Validate(x => x.Length > 0, «Вы не ввели логин»)
.RegexValidate(«Тут регулярка для логина», «Введён некорректный логин»),
null,
e => { ShowError(e.Message); success = false });
if(!success) return false;
//Дальше проверяем пароль
}
Просто иногда число проверок может быть не две, как тут, а пять и более (при каждой должно выдаваться своё сообщение), тогда такая запись оазывается и понятнее и короче.
Как вы собираетесь проверить регулярным выражением при регистрации тот факт, что такой логин уже существует? Лучше наверное сделать базовый класс валидатора, от которого уже наследовать различные валидаторы, проверяющие значения регекспами или кастом-кодом.
Если к данным привязываемся через data binding, то методы с атрибутами весьма накладно использовать. Валидировать придется по событию формы, например, нажатию кнопки, после чего вызывать методы. Здесь вилка — или усложняем логику формы и не паримся с валидацией, или упрощаем привязку данных и паримся с валидацией:) Имхо, второе лучше. По-вашему, можно навешать атрибутов на сам класс, потом его инстанс проверять в специальном валидаторе (такое решение в Enterprise Library, кстати). Но это загромождение бизнес-объекта лишними излишествами и не совсем быстро покрывается тестами.
А можно наваять свое, как у автора. Имхо, все прикольно, но я бы не стал валидировать степ-бай-степ. Кусочки валидации надо разделить на простые, и сложные. Простые — это регэкп, например, а сложные — запрос в базу данных на уникальность. Когда пользователь заполнил все что нужно и жмакнул ок, ему вывелся ValidationResult сначала с проверкой на простые условия. Когда выполнил все простые условия, идут проверки на сложные:) Но идея в статье хорошая — соединить flow of control с валидацией:)
Идея передавать функции для валидации включает возможность передачи заранее предопределенных функций, которые содержат регекспы, простые условия или запросы к марсоходу, или лямбды, как в примере.
Проблема в том, что тут нет ничего нового, никакого оригинального подхода.
Есть набор стандартных решений:
Валидатор со стандартным интерфейсом.
Последовательное выполенение проверок до первого исключения.
Исключение выводит сообщение об ошибке.
Что я вижу в статье: автор обнаружил и делится возможностью перехватывать исключения и передавать лямбды в качестве параметров. Еще он обнаружил chained method call и еще не понял почему это антипаттерн.
Почему нельзя написать несколько классов валидаторов с единым интерфесом, грубо говоря все строки проверять StringValidator, email EmailValidator и т.д. Написать прослойку, через которую будет забераться поле, что то типа InputData.get(Name). а уже прослойка на основе конфига, будет создовать объект валидатор, валидировать и результат отдовать приложению.
Можно и даже наверное нужно, но (ещё раз говорю) как правило нужно производить несколько проверок и выдавать сообщение об ошибке в зависимости от этапа проверки.
например проверка емейла в вашем варианте будет иметь вид:
несовесем, ближе к этому, ну да дело вкуса
public void validation(){
try{
isMoreThenZero();//на самом деле проверяется вместе с валидностью
isValidMail();
isUsedMail();
catch(){
}
}
Тут идея в том, что используются лямбды, которые естественным образом комбинируются. Посмотрите на финальную проверку — очень читабельно, и писать не надо много. Опять-таки, есть сомнения, нужно ли выносить валидацию в конфиг, если правила валидации меняются очень редко.
Здесь, скорее, другой недостаток — нужно писать сообщение для каждой ошибки и каждого поля. Скажем, если три поля не должны быть пустыми, для каждого нужно написать сообщение «поле не должно быть пустым».
предположим проект, с 200 сотнями форм, в каждой от 5 до 15 полей.
писать для каждой цепочка зае… ну короче быстро надоест
мой вариант
пшется около 20(если не повезет то побольше) классов валидаторов, каждый из которых делает свой специфичный стек проверок. тексты ошибки это отдельный ресурс(ы).
на каждую форму(обычно оптимизируется) прописываетя конфиг с соответствием поле->валидатор.
вот за этим и нужны конфиги.
еще был вариант каждому входному типу прописывать свой класс, одним из методов которого я вляется вылидация(иногда это делегируется другому валидатору)
в этом случае конфиг помогает автоматизировать установку соответствие имяни поля и типа.
у обоих методов свои недостатки, но отлов косяков (через unittest) был удобен
Я про это и написал. В конфиге всё-таки должны храниться условно-постоянные величины, которые могут потребовать изменения. Способ валидации (класс валидаторов) — обычно ясен уже при проектировании формы и затем меняется максимум 1 раз (если меняется).
.Validate(x => x.Length > 0, GetText(«Empty Field»)) //Собственно достать конкретное сообщение для текущей культуры
С выносом текстов проблемм нет, все эти проверки появляются в конечном продукте.
можно вместо текста ошибки возвращать некоторый строковой код, главное чтобы в программе были определены методы, которые ошибку будут обрабатывать
Эти методы будут вызываться в делегатах (ActiononSuccess, ActiononFailure)
Цепочка проверок никаким образом не зависит от того что проверяется и какие ошибки при этом возникают
Я, скорее, не о том, чтобы вынести тексты куда-то наружу, а о том, что их в принципе приходится дублировать, если одна и та же проверка выполняется для разных полей.
Хотя, если подумать, можно наверное сделать 2-3-4 готовых валидатора для распространённых случаев, и просто подставлять их как константы.
главное, чтобы проверка на сложность выводила всего лишь предупреждение, а не была обязательным критерием для акцептирования пароля системой
а то ставил я тут на игровую консоль линукс, так он потребовал обязательный сложный пароль не короче шести символов! ноги открывать таким программистам!
Вообще, я так предполагаю, эти проверки должны осуществляться только когда пользователь нажал на кнопку «Выполнить», «Отправить», «Зарегистрироваться» и в случае успеха выполнить какое-то действие.
Любые другие проверки, например на сложность пароля должны производиться пока юзер не нажал на кнопку.
Поэтому сдесь чётко: Есть ошибка — выполнить то-то, нету ошибки — то-то
Я и не говорю что это ошибка, просто это должно проверяться до того как пользователь нажал «Зарегистрироваться» или что-то в этом роде.
Поэтому эта проверка должна отдельно производиться до нажатия. После нажатия уже проверяются только ошибки которые не позволяют выполнить некоторое действие.
Основная идея написания кода — понятность, простота.
Вот ...validate().validate().validate().validate().validate().validate()… нужно разбить на отдельные независимые вызовы, чтобы их разделить, и этим облегчить чтение и отладку кода.
Chained method calls это такой антипаттерн, который усложняет понимание, связывает код, и не позволяет отлаживать код в дебагере простой простановкой брекпоинтов. В результате мы не знаем, вызывается ли следующий метод на том же самом наборе данных, или данные были изменены предыдущим методом в цепочке.
Это антипаттерн часто встречается в коде с использованием jQuery: $(".button").filter(".blue").attr(«more», «gore»).end().filter(".red").remove().end().filter()…
В результате нужно держать в памяти все точки .end(), где сбрасывается предыдущий фильтр.
Согласен, но я преследовал две цели:
1) Сократить наисание рутиного кода (Первую я всегда преследую)
2) Иметь возможность передавать методы валидации как параметры в другие методы. (Это мне сейчас особо нужно) А другим способом собрать единый метод валидации из нескольких составных частей я не вижу.
Вам не кажется, что валидатор должен проверять входной параметр на соответствие некоторым правилам, а не выполнять функции бизнес-логики (например: проверка пользователя с указанным емайлом)?
А вообще, подобные действия вписываются в паттерн Strategy
Раз уж тут тема пошла по поводу валидаторов, конечно немного не в тему) но все же) как обстаят дела с яваскрипт валидаторами для асп.нет мвц, как валидировать на клиенте? или нужно писать свои яваскрипт валидаторы?
Эту тему уже поднимали. Exceptions предназаначены для исключительных ситуаций в программе, нарушающих обычный ход выполнения, а ввод неправильного значения — это самая банальная ситуация.
Использорвание exceptions тут неоправданно и порождает путаницу. Лучше уж циклом проверять, а устаовия хранить в масиве массивов.
Это религиозный вопрос.
Отвалилась база, внешнее стройство не отвечает… все тоже банальные ситуация которые можно предусмотреть без использования ексепшенов.
Оба подходода(и комбинированный за компанию) имеют права на жизнь. И обычно решаются на уровне Нотации
Тем, что по моему, исключительная ситуация — та, при которой и упасть не грех, мол БД не работает, извиняйте, хозяин)) В редких случаях можно перехватить исклбчение и что-то исправить. А неправильно введенное поле — банальная ситуация, в ней в принципе ничего исключительного нет.
Внимательно прочитал обсуждения и нигде не увидел мысли о том, проверку нужно привязывать не к полю на форме, а к свойству того объекта, которое изменяется полем. Долго имел дело с большими системами с тоннами кода и практически везде видел одну и ту же картину: есть объекты с данными, один объект можно редактировать в разных формах и во все формы разработчики упорно копируют проверки ввода, делают подпорки что бы меньше писать кода проверок и т.д. Однако каждая смена условий превращается в увлекательное сафари «Найди, где пропустил проверку».
Сейчас пробую использовать несколько иной подход к проверкам.
Для начала делим все проверки на 2 больших класса: индивидуальные проверки свойств и проверки с использованием значений других свойств и(или) объектов.
Далее разбиваем проверки на 3 этапа: проверка введенного текста (практически все данные от пользователя идут в виде текста), конвертация текста в нужный нам тип, проверка полученного значения в контексте значений других свойств и(или) объектов. Индивидуальные проверки могут идти на всех 3-х этапах, групповые проверки практически никогда не идут на этапе 1, обычно редко идут на этапе 2, и очень часто на этапе 3.
Все проверки прописываются как атрибуты на свойстве класса.
Делаем статический класс, который имеет метод setValue(object o, string propertyname, string value) этот метод делает следующее:
1. Берет у указанного свойства объекта все атрибуты проверок и разбивает на 3 части: массив проверок текста, конвертер, массив проверок значения
2. Вызывает все проверки из массива проверок текста и собирает сообщения для показа. Если какая-то проверка не проходит, то дальше не идем.
3. Вызывает конвертер, если преобразовать данные невозможно, то выводит сообщение и дальше не идем.
4. Вызывает все проверки из массива проверки значений и собирает сообщения для показа. Если какая-то проверка не проходит, то дальше не идем.
5. Реально устанавливает значения свойства. Готово.
Вызов этого метода делается полями в форме, когда приходят данные на сервер для всех полей. Но самое вкусное тут то, что все поля в форме могут быть только textarea (даже выбор из справочника!) а так же эти проверки можно применять при обработки пришедших данных из XML файлов или любого другого источника.
Правила проверок меняются только в одном месте и автоматически распространяются на все формы, которые умеют редактировать объект, а так же на все участки кода, где есть необходимость делать проверки входящих данных.
Реализация и описания получаются достаточно простыми, а быстродействие можно сильно увеличить, если кэшировать массивы проверок.
Мы на php в веб-приложениях например можем валидацию вообще в модель перенести, тоже код в принципе в одном месте собран — в функции Validate() общего для всех моделей класса. А частные проверки можно сделать например, переопределяя метод validateField(...) (вызывается для каждого поля). Ну и предусмотреть функцию invalidate($field_name, $error_message), которая вызывается при обнаружении ошибки и пополняет соответствующий массив с ошибками валидации.
О том, как можно проверять значения, введёные пользователем