Search code examples
validationplayframework-1.xpojo

play framework validation binding invalid input value with POJO


I have a model TestModel.java which contains a field declared like below

@Required
@IntegerAnnotation
@Column (length = 4, nullable = false)
public Integer sort;

For the default validation annotation of Play doesn't support Integer check, so I create an annotation validation for checking the input value is an Integer or not.

The IntegerAnnotation.java :

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(checkWith = IntegerCheck.class)
public @interface IntegerAnnotation {
    String message() default IntegerCheck.message;}
}

and this annotation refers to an implementations of IntegerCheck.java

public class IntegerCheck extends AbstractAnnotationCheck<IntegerAnnotation>{

final static String message = "validation.integer";

@Override
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator) throws OValException {
    // TODO Auto-generated method stub

    if(value == null) return false;
    return isInteger(value.toString());
}

private static boolean isInteger(String s) {
    return isInteger(s,10);
}

private static boolean isInteger(String s, int radix) {
    if(s.isEmpty()) return false;
    for(int i = 0; i < s.length(); i++) {
        if(i == 0 && s.charAt(i) == '-') {
            if(s.length() == 1) return false;
            else continue;
        }
        if(Character.digit(s.charAt(i), radix) < 0) return false;
    }
    return true;
}
}

And there are two actions in the controller for create or update the TestModel, method create and method update.

public static void create(@Valid TestModel testModel){
    if(validation.hasErrors()) {
        params.flash();
        validation.keep();
        blank();
    }
}

public static void update(@Valid TestModel testModel) {

    if(validation.hasErrors()) {
        params.flash();
        validation.keep();
    }
    else{
        testModel.save();
        show(sys5000.id);
    }
}

I found when the field sort is not entered with integer value, the value will be null in the create method, that's why I put a if null condition in the IntegerCheck.class.

Thus, if the value of filed sort is wrong typed, it detects null and return false. Though it's not what I expect it will use the wrong typed value to verify, it worked...sort of.

The problem is in the update method. For when updating an instance of TestModel, it won't show the wrong typed value, but the original retrieved value from database instead. It's really a problem, for it will always return true if the retrieved data from database is already an integer. Then the validation won't do the work.

And the questions are:

  1. Any advice for this validation strategy? I think maybe I'm apart from the right way to verify the Integer value validation.

  2. How can I retrieve the wrong typed value from the action, or it's just not possible, for it's already not a valid data type of that field.


Solution

  • The behavior that you see in the update method is the way Play works!

    Here is the relevant section from the documentation :

    ... When Play finds the id field, it loads the matching instance from the database before editing it. The other parameters provided by the HTTP request are then applied.

    So in your case, when the sort property is null during an update, the value in the database is used.

    Now if I were to try to achieve what you are trying to do, I would propably do it this way :

    In my model

    @Required
    @CheckWith(IntegerCheck.class)
    public int sort; // using primitive here to avoid null values.
    
    static class IntegerCheck extends Check {
    
        public boolean isSatisfied(Object model, Object sort) {
            int fieldValue = (int) sort; // here you have the value as int
    
            // Here you would check whatever you want and return true or false.
    
            return ... 
        }
    
    }