JAX-RS - Performing Entity validation by using custom annotation

[Updated: Aug 18, 2017, Created: Aug 18, 2017]

Other than using @Valid annotation (last tutorial), JAX-RS specification allows us to define custom constraint annotations to be used with entity parameter. Let's understand that with an example.

Example

A JAX-RS resource

@Path("/")
public class UserResource {

  @POST
  @Path("users")
  public String createUser(@ValidRole User user) {
      System.out.println("-- in createUser() method --");
      return String.format("User created : %s%n", user);
  }
    .............
}

As seen above, we did not use @Valid annotation with User, but instead we used a custom annotation @ValidRole.

Writing the custom constraint annotation

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
      ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UserRoleValidator.class)
@Documented
public @interface ValidRole {
  String message () default "Not a valid User Role. Found: ${validatedValue}";

  Class<?>[] groups () default {};

  Class<? extends Payload>[] payload () default {};
}
public class UserRoleValidator implements
      ConstraintValidator<ValidRole, User> {

  private static final List<String> ALLOWED_ROLES = Arrays.asList("admin", "super-user");

  @Override
  public void initialize(ValidRole constraintAnnotation) {
  }

  @Override
  public boolean isValid(User user, ConstraintValidatorContext context) {
      return ALLOWED_ROLES.contains(user.getRole());
  }
}

The Bean

@XmlRootElement
public class User {
  @NotNull
  private String name;
  @NotNull
  private String role;
  @Email
  private String email;
    .............
}

Note that we used hibernate provided @Email constraint to validate standard email format.


To try examples, run embedded tomcat (configured in pom.xml of example project below):

mvn tomcat7:run-war

JAX-RS client

public class MyClient {

  public static void main(String[] args) throws Exception {
      User user = new User();
      user.setName(null);
      user.setEmail("user-example.com");
      user.setRole("visitor");

      Client client = ClientBuilder.newBuilder().build();
      WebTarget target =
              client.target("http://localhost:8080/users");
      Response response = target.request()
                              .post(Entity.entity(user,
                                      MediaType.TEXT_XML), Response.class);
      System.out.println(response.readEntity(String.class));
  }
}

Output

createUser.arg0 Not a valid User Role. Found: User{name='null', role='visitor', email='user-example.com'}

Note that in above output the constraint annotations defined on the User fields were not applied. For that to happen we need to use @Valid annotation as well (next section).

Combined validation with @ValidRole and @Valid

@Path("/")
public class UserResource {
    .............
  @POST
  @Path("users2")
  public String createUser2(@Valid @ValidRole User user) {
      System.out.println("-- in createUser2() method --");
      return String.format("User created : %s%n", user);
  }
}
public class MyClient2 {

  public static void main(String[] args) throws Exception {
      User user = new User();
      user.setName(null);
      user.setEmail("user-example.com");
      user.setRole("visitor");

      Client client = ClientBuilder.newBuilder().build();
      WebTarget target =
              client.target("http://localhost:8080/users2");
      Response response = target.request()
                              .post(Entity.entity(user,
                                      MediaType.TEXT_XML), Response.class);
      System.out.println(response.readEntity(String.class));
  }
}

Output

createUser2.arg0.email not a well-formed email address
createUser2.arg0.name may not be null
createUser2.arg0 Not a valid User Role. Found: User{name='null', role='visitor', email='user-example.com'}

Notes

  • According to Java Bean validation specification (JSR-303/349), we can create custom annotation as explained here.
  • In above example, we can place @ValidRole annotation on User class instead of using it with resource method parameter. See an example of class level constraint here. In that case, we have to use @Valid with entity parameter which will invoke both default field validations and our custom class level validator. That means, we will not have the choice to invoke only our custom validator as we did in the first part above (UserResource#createUser resource method).

Example Project

Dependencies and Technologies Used :

  • jersey-server 2.25.1: Jersey core server implementation.
  • jersey-container-servlet 2.25.1: Jersey core Servlet 3.x implementation.
  • jersey-bean-validation 2.25.1: Jersey extension module providing support for Bean Validation (JSR-349) API.
  • JDK 1.8
  • Maven 3.3.9

Entity Parameter Custom Validation Example Select All Download
  • jaxrs-entity-custom-validation-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also