4.2.13.2. Запуск валидации
Валидация в UI

Компоненты Generic UI, соединенные с источником данных, получают экземпляр BeanValidator для проверки значения. Валидатор вызывается из метода Component.Validatable.validate(), реализуемого компонентом, и может выбрасывать исключение CompositeValidationException, содержащее набор объектов нарушений.

Стандартный валидатор может быть программно удален или проинициализирован другой группой ограничений:

public class Screen extends AbstractWindow {

    @Inject
    private TextField field1;
    @Inject
    private TextField field2;

    public void init(Map<String, Object> params) {
        // Completely remove bean validation from the UI component
        field1.getValidators().stream()
                .filter(validator -> validator instanceof BeanValidator)
                .forEach(textField::removeValidator);

        // Here validators will check only constraints with explicitly set UiComponentChecks group, because
        // the Default group will not be passed
        field2.getValidators().stream()
                .filter(validator -> validator instanceof BeanValidator)
                .forEach(validator -> {
                    ((BeanValidator) validator).setValidationGroups(new Class[] {UiComponentChecks.class});
                });
    }
}

По умолчанию, BeanValidator содержит две группы: Default и UiComponentChecks.

Если атрибут сущности аннотирован @NotNull без группы ограничений, он будет помечен как обязательный в метаданных, и UI-компоненты работающие с данным атрибутом через источник данных, будут иметь свойство required = true.

Компоненты DateField и DatePicker автоматически устанавливают свои свойства rangeStart и rangeEnd в соответствии с аннотациями @Past и @Future, не учитывая текущее время.

Экраны редактирования выполняют валидацию ограничений уровня класса при коммите, если ограничения включают группу UiCrossFieldChecks и все проверки ограничений уровня атрибутов прошли успешно. Валидацию данного типа можно отключить с помощью свойства экрана crossFieldValidate в XML-дескрипторе или в контроллере:

<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://editorCaption"
        class="com.company.demo.web.task.TaskEdit"
        datasource="taskDs"
        crossFieldValidate="false">
    <!-- ... -->
</window>
public class TaskEdit extends AbstractEditor<Task> {
    @Override
    public void init(Map<String, Object> params) {
        setCrossFieldValidate(false);
    }
}
Валидация сервисов Middleware

Сервисы среднего слоя выполняют валидацию параметров и результатов методов, если метод имеет аннотацию @Validated в интерфейсе сервиса. Например:

public interface TaskService {
    String NAME = "demo_TaskService";

    @Validated
    @NotNull
    String completeTask(@Size(min = 5) String comment, @NotNull Task task);
}

Аннотация @Validated может содержать указание групп ограничений для применения подмножества имеющихся ограничений. Если группы не указаны, то по умолчанию используются следующие:

  • Default и ServiceParametersChecks - для параметров методов

  • Default и ServiceResultChecks - для возвращаемых результатов методов

При возникновении ошибок валидации выбрасываются исключения MethodParametersValidationException и MethodResultValidationException.

При выполнении некоторой специфической программной валидации в сервисе, можно использовать исключение CustomValidationException для того, чтобы клиенты получали информацию об ошибках в том же формате что и от стандартной валидации. Это может быть особенно актуально для клиентов REST API.

Валидация в REST API

Универсальный REST API автоматически выполняет bean validation для действий создания и изменения сущностей. Ошибки валидации передаются клиенту следующим образом:

  • MethodResultValidationException и ValidationException порождают HTTP статус 500 Server error

  • MethodParametersValidationException, ConstraintViolationException и CustomValidationException порождают HTTP статус 400 Bad request

  • Тело ответа с Content-Type: application/json будет содержать список объектов со свойствами message, messageTemplate, path и invalidValue, например:

    [
        {
            "message": "Invalid email: aaa",
            "messageTemplate": "{msg://com.company.demo.entity/Customer.email.validationMsg}",
            "path": "email",
            "invalidValue": "aaa"
        }
    ]
    • path содержит путь к невалидному атрибуту в валидируемом графе объектов

    • messageTemplate содержит сторку, заданную в атрибуте message аннотации

    • message содержит сообщение ошибки валидации

    • invalidValue возвращается только если тип значения один из следующих: String, Date, Number, Enum, UUID.

Программная валидация

Bean validation можно запустить программно используя интерфейс инфраструктуры BeanValidation, доступный и на middleware и на клиентском уровне. Через данный интерфейс необходимо получить реализацию javax.validation.Validator, которая и запускает валидацию. Результатом валидации является набор объектов типа ConstraintViolation. Например:

@Inject
private BeanValidation beanValidation;

public void save(Foo foo) {
    Validator validator = beanValidation.getValidator();
    Set<ConstraintViolation<Foo>> violations = validator.validate(foo);
    // ...
}