Валидации
Валидации позволяют проверить корректность входных данных для выполнения операций над объектом и виджетом.
Валидация объекта
Валидация объекта позволяет задать единые правила корректности входных данных для любых операций и выборок.
Все валидации объекта перечисляются в элементе <validations>
:
<object>
<validations>
... <!-- Валидации объекта -->
</validations>
</object>
Способы валидаций
Существует два основных способа валидаций:
- Проверка входных данных по условиям
- Проверка целостности данных на уровне БД
Проверка входных данных по условиям
По условиям можно проверить корректность значения поля, его взаимосвязи с другими полями.
Проверка по условию записывается в элементе <condition>
:
<validations>
<condition id="checkAdult"
message="Запрещено для детей. Ваш возраст {age}, что меньше 18."
field-id="age">age >= 18</condition>
</validations>
Атрибут id
задаёт идентификатор валидации в объекте.
В дальнейшем, через id
на валидацию можно будет сослаться в тех местах,
где она должна сработать.
В теле элемента <condition>
задается условие на языке javaScript.
Атрибутом field-id
можно задать поле, под которым появится сообщение валидации.
Если условие будет верным (вернёт true
), то валидация пройдёт успешно.
Если же будет неверным (вернёт false
), то появится сообщение message
.
В сообщении можно использовать ссылки на поля входных данных. Ссылки записываются в фигурных скобках.
Проверка целостности данных на уровне БД
Чтобы проверить целостность данных, необходимо сделать запрос к источнику данных с помощью провайдеров.
Проверка целостности данных задаётся в элементе <constraint>
:
<validations>
<constraint id="checkUniqueName"
message="Пользователь с таким именем уже существует в системе"
server-moment="before-operation" severity="danger"
result="[0].get('CNT') == 0">
<invocation>
<sql>
SELECT count(1) as cnt
FROM user
WHERE username = :username
</sql>
</invocation>
<in>
<field id="username"/>
</in>
</constraint>
</validations>
Обращение к провайдеру данных задаётся в элементе <invocation>
.
В элементе <in>
задаются входные параметры провайдера.
Одновременно, эти параметры являются триггерами срабатывания валидации.
В элементе <out>
задаются выходные параметры провайдера.
На них можно ссылаться в сообщении валидации.
В атрибуте result
задаётся условие проверки результата.
Условие записывается на языке SpEL.
Если условие будет верным (вернёт true
), то валидация пройдёт успешно.
Если же будет неверным (вернёт false
), то появится сообщение message
.
Проверка обязательности заполнения
Чтобы проверить обязательность заполнения поля,
можно поставить атрибут required="true"
для входящего параметра операции:
<operation>
<in>
<field id="name" required="true"/>
</in>
</operation>
В этом случае для поля выполняется стандартная проверка заполненности и выводится сообщение.
Если требуется вывести нестандартное сообщение, можно создать специальную валидацию <mandatory>
:
<validations>
<mandatory id="requiredName"
field-id="name"
message="Имя должно быть заполнено"/>
</validations>
Серьезность валидации
Валидации могут иметь разный уровень серьезности:
Уровни серьёзности валидаций
Уровень | Описание | Цвет сообщения |
---|---|---|
danger | Прерывает выполнение действий. | Красный |
warning | Предупреждает об отклонениях в прошедшем действии. | Желтый |
info | Информирует об особенностях прошедших или будущих действий. | Голубой |
success | Даёт положительную обратную связь. | Зеленый |
Уровень задаётся в атрибуте severity
:
<validations>
<condition level="warning">
...
</condition>
</validations>
По умолчанию danger
.
Момент срабатывания на сервере
Валидации могут быть вызваны на сервере в разные моменты жизненного цикла объекта.
Серверные моменты вызова валидации
Момент | Описание | Случаи использования |
---|---|---|
before-operation | Перед выполнением операции | Проверка входных данных |
before-query | Перед получением данных выборки | Проверка ограничений фильтрации |
after-success-query | После успешного получения данных выборки | Информирование о результатах |
after-fail-query | После неудачного получения данных выборки | Информирование об ошибках |
after-success-operation | После успешного выполнения операции | Положительная обратная связь |
after-fail-operation | После неудачного выполнения операции | Отрицательная обратная связь |
Момент валидации задаётся атрибутом server-moment
:
<validations>
<condition server-moment="before-query">
...
</condition>
</validations>
По умолчанию before-operation
при severity равном danger
или warning
,
иначе - after-success-operation
Отключение валидации
Валидацию можно выключить полностью или при определенных условиях.
За это отвечает атрибут enabled
.
Атрибут enabled
принимает true
или false
,
или выражение javaScript записанное в фигурных скобках:
<validations>
<mandatory id="requiredMaidenNameForWomen"
field-id="maidenName"
enabled="{gender == 'woman'}"/>
</validations>
Валидация операций
Для операций можно ограничить список валидаций объекта применимых к ней. Ограничить можно белым и черным списком.
В случае белого списка только валидации перечисленные в нём будут применимы к операции:
<operation id="delete">
<validations white-list="checkDependencies"/>
</operation>
В случае черного списка все валидации, кроме перечисленных в нём, будут применимы к операции:
<operation id="create">
<validations black-list="checkDependencies"/>
</operation>
Вложенная валидация операций
В операции можно задать и саму валидацию:
<operation>
<validations>
<condition>...</condition>
<constraint>...</constraint>
</validations>
</operation>
Вложенные валидации применимы к операции всегда и могут сочетаться с белыми и черными списками валидаций объекта.
Валидация с диалогом выбора
В N2O есть возможность использовать диалоги для подтверждения действий пользователя. Это особенно полезно в ситуациях, когда мы хотим убедиться, что пользователь точно уверен в выполняемом действии.
Настраиваемый диалог
Диалог настраивается таким же образом, что и любой другой тип валидации.
Отличие диалога от <constraint>
валидации в наличии настраиваемого тулбара (меню кнопок).
В качестве действий кнопок можно использовать все стандартные действия.
Вызов диалога возможен в любой момент: до выполнения операции (before-operation
),
после выполнения успешной (after-success-operation
) и после операции,
закончившейся ошибкой (after-fail-operation
).
Для того, чтобы не допустить повторного вызова диалога, можно воспользоваться
атрибутом enabled
и дополнительным параметром:
<operation id="create">
<invocation>...</invocation>
<in>
<!-- параметр для отключения вызова диалога -->
<field id="validated" param="validated"/>
</in>
<validations>
<!-- диалог не вызывается, если validated=true -->
<dialog id="dialog" server-moment="before-operation"
result="#this < 3"
enabled="{typeof(validated) === 'undefined' || validated == false}"
message="Достигнут лимит по количеству пользователей.
Вы уверены, что хотите добавить еще одного?">
<invocation>
...
</invocation>
<toolbar>
<button id="yes" label="Да">
<invoke operation-id="create">
<!-- отправка validated=true, после подтверждения -->
<header-param name="validated" value="true"/>
</invoke>
</button>
<button label="Отмена">
<close/>
</button>
</toolbar>
</dialog>
</validations>
</operation>
Использование диалога с вызовом провайдера данных и без
Вызовы провайдера данных не являются обязательными в диалоге.
Отсюда будут отличаться и множество параметров, используемых для
вычисления корректности валидации в атрибуте result
.
- В случае вызова провайдера данных в качестве таких параметров, будут использоваться параметры, приходящие с сервера
- В случае отсутствия - входящие параметры. Таким образом мы можем осуществить предварительную проверку данных перед отправкой их на сервер
Валидация полей
Валидации объекта можно прикреплять к полям формы. В этом случае сообщение валидации появится под выбранным полем.
Для прикрепления к полю используется атрибут field-id
:
<validations>
<condition id="checkAdult"
field-id="age">...</condition>
</validations>
В самих полях так же можно указать список валидаций, сообщения которых прикрепятся к полю.
Это задаётся в элементе <validations>
:
<input-text id="age">
<validations white-list="checkAdult"/>
</input-text>
Вложенная валидация поля
В поле можно задать и саму валидацию:
<input-text>
<validations>
<condition>...</condition>
<constraint>...</constraint>
</validations>
</input-text>
Вложенные валидации в поле выполняются всегда и могут сочетаться с белым списком валидаций объекта.
Bean Validation (JSR 303)
Стандарт JSR303 Bean Validation позволяет валидировать java сущности по аннотациям.
N2O умеет считывать аннотации с bean сущностей и генерировать по ним <constraint>
и <condition>
валидации.
Для использования нужно подключить библиотеку n2o-validation
:
<dependency>
<groupId>net.n2oapp.framework</groupId>
<artifactId>n2o-validation</artifactId>
<version>${n2o.version}</version>
</dependency>
Генерация constraint валидаций
Чтобы сгенерировать валидации по аннотациям JSR303, необходимо подключить схему расширений:
http://n2oapp.net/framework/config/schema/bean-validation-1.0
И включить автоматическую генерацию валидаций с помощью атрибута generate
:
<object xmlns="http://n2oapp.net/framework/config/schema/object-4.0"
xmlns:bv="http://n2oapp.net/framework/config/schema/bean-validation-1.0"
entity-class="com.example.MyEntity"
bv:generate="true">
...
</object>
После этого в объекте будут автоматически созданы <constraint>
валидации по сущности entity-class
.
Генерация работает и для кастомных валидаторов, если для них реализован ConstraintValidator.
Идентификаторы <constraint>
валидаций генерируется по следующему шаблону:
$bv_{id}
, где id
идентификатор in параметра.
Например, $bv_name
.
Для корректной работы автоматической генерации валидаций требуется, чтобы поле с одним и тем же id всегда мапилось в одно и то же поле сущности.
Генерация condition валидаций
Валидации <condition>
работают на JavaScript выражениях и
выполняются быстрее <constraint>
, так как не обращаются к серверу.
По умолчанию <condition>
валидации генерируются только для стандартных
аннотаций: @Max
, @Min
, @Pattern
, @Size
, @Future
, @Past
.
Для генерации валидаций по собственным аннотациям требуется реализовать
интерфейс ConditionBeanValidator
и зарегистрировать его в качестве
Spring Bean.
/**
* Проверяет, что ввели только латинские символы
**/
public class LatinValidator implements ConditionBeanValidator<Latin> {
/**
* Конвертирует JSR303 валидацию в JavaScript выражение
* @param annotation аннотация
* @param param свойство в модели виджета
* @return condition валидация
*/
@Override
public N2oCondition evaluate(Latin annotation, String param) {
N2oCondition condition = new N2oCondition();
condition.setMessage("Допустимы только латинские буквы");
condition.setExpression("/[a-zA-Z]/i.test(" + param + ")");
return condition;
}
@Override
public Class<Latin> getType() {
return Latin.class;
}
}
В объекте N2oCondition
необходимо задать только expression
и message
поля.
Остальные поля установит N2O при генерации.
Идентификаторы <condition>
валидаций генерируется по следующему шаблону:
$bv_{id}_{annotation}
, где id
идентификатор in параметра, а annotation
простое имя класса аннотации.
Например, $bv_name_latin
.
Автоматическая установка обязательных полей
При включенной генерации валидаций bv:generate="true"
, по аннотациям @NotNull
автоматически заполняются атрибуты required="true"
у полей объекта.
Точечная валидация поля
Автоматическую генерацию валидаций не всегда удобно использовать, из-за того, что она применяется ко всем полям без исключений. Иногда может потребоваться валидация только по конкретным полям или аннотациям.
Для таких случаев можно использовать специальный провайдер данных bv:validate
.
<object xmlns="http://n2oapp.net/framework/config/schema/object-4.0"
xmlns:bv="http://n2oapp.net/framework/config/schema/bean-validation-1.0.xml"
entity-class="com.example.MyEntity">
...
<validations>
<constraint id="myBeanValidation"
message="Произошла ошибка {mess} со значением {invalid}"
result="#this == null" severity="danger">
<invocation>
<bv:validate
property="name"
annotation-class="javax.validation.constraints.Pattern"/>
</invocation>
<in>
<field id="name" mapping="name"/>
</in>
<out>
<field id="mess" mapping="message"/>
<field id="invalid" mapping="invalidValue"/>
</out>
</constraint>
</validations>
</object>
В атрибуте property
задаётся поле сущности,
которое требуется проверить по JSR303 валидациям.
По умолчанию будут проверены все аннотации на этом поле.
Если требуется проверить только одну аннотацию, можно указать её класс через атрибут annotation-class
.
Результатом вызова будет объект класса ConstraintViolation
,
если были нарушения, или null
, если нарушений не было.