4.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:

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});
                });
    }
}

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 and @Future annotations, but without considering time.

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 AbstractEditor<Task> {
    @Override
    public void init(Map<String, Object> params) {
        setCrossFieldValidate(false);
    }
}
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 an 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);
    // ...
}