Search code examples
javajerseyjacksonhibernate-validator

Hibernate Validator With Validation groups for Serialisation With Write Access


I have an endpoint where a user can register with her email/password. However I, want to check that the password is not empty, but I also want to only deserialize the password, because I do not want to send it back to the client.

Domain

public class User {
  @NotEmpty
  private String email;
  @JsonProperty(access = Access.WRITE_ONLY)
  @NotEmpty
  private String password;

  // Getters and setters
  ...
}

Endpoint

@Path("/register")
@POST
public Response register(@Valid User user) {
  ...
}

Instead of the Access.WRITE_ONLY I've also tried @JsonIgnore on the getter and @JsonProperty on the setter.

The problem is Hibernate Validator keeps complaining that the password is empty, even when I POST a user with password set:

"{"errors":["password may not be empty"]}"

How can I fix this? Or do I have to implement my own NotEmpty validation logic in the endpoint?


Solution

  • This can be done using validation groups. Here is how you would achieve this:

    public class GroupValidationTest {
    
    
        public static void main(String[] args) {
            Validator v = Validators.newValidator();
    
    
            Model m = new Model();
            m.user = "Harry";
            m.password = "Potter";
    
            Set<ConstraintViolation<Model>> validate = v.validate(m, INPUT.class);
            System.out.println(validate.size());
    
            validate = v.validate(m, INPUT.class, OUTPUT.class);
            System.out.println(validate.size());
    
            validate = v.validate(m, OUTPUT.class);
            System.out.println(validate.size());
    
            m.password = null;
    
            validate = v.validate(m, INPUT.class, OUTPUT.class);
            System.out.println(validate.size());
    
            validate = v.validate(m, OUTPUT.class);
            System.out.println(validate.size());
        }
    
        public static class Model {
    
            @NotEmpty(groups={INPUT.class, OUTPUT.class})
            public String user;
    
            @NotEmpty(groups={INPUT.class})
            public String password;
    
        }
    
        public interface INPUT {}
        public interface OUTPUT {}
    
    }
    

    This outputs:

    0 -> Full object, validate INPUT 
    0 -> Full object, validate INPUT + OUTPUT
    0 -> Full object, validate OUTPUT
    1 -> null password, validate INPUT + OUTPUT
    0 -> null password, validate OUTPUT
    

    Explenation:

    Hibernate validator framework supports groups for validation constraints. They are used to tell the validator which groups to validate in a specific validation attempt. You can read all about it here:

    https://docs.jboss.org/hibernate/validator/4.2/reference/en-US/html/validator-usingvalidator.html#example-group-interfaces

    What I did in my code example is:

    • Define 2 groups INPUT and OUTPUT
    • Mark the user property to be validated for both groups
    • Mark the password property to be validated only on input

    For the REST aspect (different question), usually validation of input and ouput is done by an interceptor, typically the MessageBodyReader and Writer class. These are responsible for reading user input and writing it out. You can read about that on the jersey docs page.

    You can then implement your own validation reader/writer that knows to only validate INPUT when reading user input, while only validating OUTPUT when writing the serialised body back.

    I hope that helps,

    artur