3.2.13.2. Running Validation
Validation in UI

Generic UI components connected to a datasource get an instance of BeanValidator to check the field value. The validator is invoked from the Component.Validatable.validate() method implemented by the visual component and can throw the CompositeValidationException exception that contains the set of violations.

The standard validator can be removed or initialized with a different constraint group:

@UiController("sample_NewScreen")
@UiDescriptor("new-screen.xml")
public class NewScreen extends Screen {

    @Inject
    private TextField<String> field1;
    @Inject
    private TextField<String> field2;

    @Subscribe
    protected void onInit(InitEvent event) {
        field1.getValidators().stream()
                .filter(BeanPropertyValidator.class::isInstance)
                .forEach(field1::removeValidator); (1)

        field2.getValidators().stream()
                .filter(BeanPropertyValidator.class::isInstance)
                .forEach(validator -> {
                    ((BeanPropertyValidator) validator).setValidationGroups(new Class[] {UiComponentChecks.class}); (2)
                });
    }
}
1 Completely remove bean validation from the UI component.
2 Here validators will check only constraints with explicitly set UiComponentChecks group, because the Default group will not be passed.

By default, BeanValidator has both Default and UiComponentChecks groups.

If an entity attribute is annotated with @NotNull without constraint groups, it will be marked as mandatory in metadata and UI components working with this attribute through a datasource will have required = true.

The DateField and DatePicker components automatically set their rangeStart and rangeEnd properties by the @Past, @PastOrPresent, @Future, @FutureOrPresent annotations.

Editor screens perform validation against class-level constraints on commit if the constraint includes the UiCrossFieldChecks group and if all attribute-level checks are passed. You can turn off the validation of this kind using the crossFieldValidate property of the screen in the screen XML descriptor or in the controller:

<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 StandardEditor<Task> {
    @Subscribe
    protected void onInit(InitEvent event) {
        setCrossFieldValidate(false);
    }
}
Validation in DataManager

DataManager can perform validation of saved entity instances. The following parameters affect the validation:

  • The cuba.dataManagerBeanValidation application property sets the global default for whether the validation is performed.

  • You can override the global default by providing a CommitContext.ValidationMode value to CommitContext when using DataManager.commit() method or to DataContext.PreCommitEvent when saving data in a UI screen.

  • You can provide a list of validation groups to CommitContext and to DataContext.PreCommitEvent to apply only a subset of defined constraints.

Validation in Middleware Services

Middleware services perform validation of parameters and results if a method has annotation @Validated in the service interface. For example:

public interface TaskService {
    String NAME = "demo_TaskService";

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

The @Validated annotation can specify constraint groups to apply a certain set of constraints. If no groups are specified, the following are used by default:

  • Default and ServiceParametersChecks - for method parameters

  • Default and ServiceResultChecks - for method return value

The MethodParametersValidationException and MethodResultValidationException exceptions are thrown on validation errors.

If you perform some custom programmatic validation in a service, use CustomValidationException to inform clients about validation errors in the same format as the standard bean validation does. It can be particularly relevant for REST API clients.

Validation in REST API

Universal REST API automatically performs bean validation for create and update actions. Validation errors are returned to the client in the following way:

  • MethodResultValidationException and ValidationException cause 500 Server error HTTP status

  • MethodParametersValidationException, ConstraintViolationException and CustomValidationException cause 400 Bad request HTTP status

  • Response body with Content-Type: application/json will contain a list of objects with message, messageTemplate, path and invalidValue properties, for example:

    [
        {
            "message": "Invalid email: aaa",
            "messageTemplate": "{msg://com.company.demo.entity/Customer.email.validationMsg}",
            "path": "email",
            "invalidValue": "aaa"
        }
    ]
    • path indicates a path to the invalid attribute in the validated object graph

    • messageTemplate contains a string which is defined in the message annotation attribute

    • message contains an actual value of the validation message

    • invalidValue is returned only if its type is one of the following: String, Date, Number, Enum, UUID.

Programmatic Validation

You can perform bean validation programmatically using the BeanValidation infrastructure interface, available on both middleware and client tier. It is used to obtain a javax.validation.Validator implementation which runs validation. The result of validation is a set of ConstraintViolation objects. For example:

@Inject
private BeanValidation beanValidation;

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