Skip to content

Validation

Geert Bevin edited this page Oct 22, 2023 · 4 revisions

RIFE2's validation API centralizes its operations around beans.

You register a series of ValidationRules, then validate them against the current state of your bean, potentially generating a list of ValidationErrors. Each error applies to a validated property and has a symbolic identifier that can be inspected.

Most of this is automated through the use of Constraints, which will register appropriate corresponding validation rules.

You can however also work with the validation API directly, let's take a look at how that works.

Adding validation rules

We'll take a step back and not use constraints for the Person class in our ongoing example.

Just adding validation rules could then look like this:

public class Person extends Validation {
    private Integer id_;
    private String name_;

    protected void activateValidation() {
        addRule(new ValidationRuleNotNull("name"));
        addRule(new ValidationRuleLimitedLength("name", 0, 30));
    }

    public void    setId(Integer id)    { id_ = id; }
    public Integer getId()              { return id_; }
    public void    setName(String name) { name_ = name; }
    public String  getName()            { return name_; }
}

The person.validate() call will behave exactly the same as before, but since no constraints were added, the GenericQueryManager's database table queries will not be generated with those in mind.

You can create your own validation rules by implementing the ValidationRule interface and completely customize a bean's validation logic. Constraints are provided by RIFE2 as they need to be understood throughout the full stack, so their customization opportunities are less flexible.

Performing validation

Calling validate() evaluates the active validation rules and accumulates validation errors. When validation errors are present, false will be returned and the errors can be obtained through getValidationErrors().

The previous section about constraints has an example demonstrating this, here are the extracted relevant lines:

    var person = c.parametersBean(Person.class);
    if (!person.validate()) {
        // person is invalid
        person.getValidationErrors().forEach(e ->
            // do something with each validation error
        );
    } else {
        // person is valid
    }

TIP : You can find a complete example of how to use validation in the RIFE2 GitHub repository.

You can also perform any validation logic of your own and add custom validation errors through addValidationError(ValidationError). This allows all the validation results to end up in one collection that can be handled the same way.

Note: Validation is not reset with you call validate(), allowing you to accumulate errors in different contexts. Use resetValidation() to start validation over for a particular bean instance.

Grouping validation

Sometimes rules only apply in certain situations, you could decide to dynamically add different validation rules, but RIFE2 also provides a way to group rules and only validate specific groups.

For instance, imagine that our Person class also has an email address, but that it is validated in a second step. These could then be the validation rules:

    // ...
    protected void activateValidation() {
        addGroup("step1")
            .addRule(new ValidationRuleNotNull("name"))
            .addRule(new ValidationRuleLimitedLength("name", 0, 30));
        
        addGroup("step2")
            .addRule(new ValidationRuleNotNull("email"))
            .addRule(new ValidationRuleEmail("email"))
            .addRule(new ValidationRuleLimitedLength("email", 0, 50));
    }
    // ...

Note that you can also use constraints this way:

    // ...
    protected void activateValidation() {
        addGroup("step1")
            .addConstraint(new ConstrainedProperty("name")
                .maxLength(20)
                .notNull(true));

        addGroup("step2")
            .addConstraint(new ConstrainedProperty("email")
                .email(true)
                .maxLength(50)
                .notNull(true));
    }
// ...

Groups are currently only used for validation, and you have two options:

  • either you only validate the rules for a specific group
  • or you focus on the errors that only apply to a specific group

For example, validating a group could look like this:

    var person = new Person();
    person.validateGroup("step1");
    // do something if errors occurred
        
    person.resetValidation();
    person.validateGroup("step2");
    // do something if errors occurred

And focusing on the errors of a particular group could look like this:

    var person = new Person();
    person.validate();
    person.focusGroup("step1");
    // do something if errors occurred
        
    person.resetValidation();
    person.validate();
    person.focusGroup("step2");
    // do something if errors occurred

Next learn more about Error Reporting