简体   繁体   中英

Java Play! Framework form validation order

On Play! Framework 2.6 I have implemented the following form which requires a custom validation:

@Validate
public class MachineRegistrationForm implements Validatable<List<ValidationError>> {

    @Required
    private String field1;

    @Required
    private String field2;

    // other fields, getters and setters

    @Override
    public List<ValidationError> validate() {

        // validation on field1 and field2

    }
}

Looks like Play performs my custom validation before checking if the @Required fields field1 and field2 actually contains some values, forcing me to check if the values are null to avoid NullPointerExceptions. Am I following the wrong approach to custom validation or is this a Play! unintended behaviour?

TLDR: RTFM ;-)

In your example the validate() method is not evaluated before the two @Required constraints: The three constraints are called simultaneously - so basically there is no guaranteed order what will run first. This is also documented in the Play Framework documentation :

Also be aware that in this example the validate method and the @Constraints.Required constraint will be called simultaneously - so the validate method will be called no matter if @Constraints.Required was successful or not (and vice versa). You will learn how to introduce an order later on.

If you carefully read the docs you will then find the section "Defining the order of constraint groups" :

You can validate groups in sequences. This means groups will be validated one after another - but the next group will only be validated if the previous group was validated successfully before. (However right now it's not possible to determine the order of how constraints will be validated within a group itself...)

To make that work for you example let's great the group...

public interface First { }

...and...

public interface Second { }

...and you would have to great following group sequence:

import javax.validation.GroupSequence;
import javax.validation.groups.Default;

@GroupSequence({ Default.class, First.class, Second.class })
public interface OrderedChecks { }

Then you have to add the groups to your constraints:

@Validate(groups = {Second.class})
public class MachineRegistrationForm implements Validatable<List<ValidationError>> {

    @Required // Default group is Default.class
    private String field1;

    @Required(groups = {First.class})
    private String field2;

    // other fields, getters and setters

    @Override
    public List<ValidationError> validate() {

        // validation on field1 and field2

    }
}

Now you can trigger the validation with the defined order:

Form<MachineRegistrationForm> form = formFactory().form(MachineRegistrationForm.class, OrderedChecks.class).bindFromRequest();

Now field1 will be checked first and only if it succeeded then field2 will be checked and only if that one succeeded as well the validate method will be checked. You could also remove (groups = {First.class}) from field2 (so it has the Default.class group by default as well) or , the other way around, add (groups = {First.class}) to field1 - in both cases now the two fields would be evaluated simultaneously - and only if both succeed then validate would be evaluted.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM