JAX-RS - Mapping ConstraintViolationException with ExceptionMapper

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

Bean Validation runtime throws ConstraintViolationException on validation failure. By default Jersey uses an ExceptionMapper to return 'Bad Request' (400) error as we saw in the last example. If interested, you can check out this source code to see how Jersey implemented that.

In this example (which is an enchantment of the last example), we will implement our own ExceptionMapper to return custom validation error message.

Example

Writing the ExceptionMapper

@Provider
public class MyExceptionMapper
               implements ExceptionMapper<ConstraintViolationException> {

  @Override
  public Response toResponse(final ConstraintViolationException exception) {
      return Response.status(Response.Status.BAD_REQUEST)
                     .entity(prepareMessage(exception))
                     .type("text/plain")
                     .build();
  }

  private String prepareMessage(ConstraintViolationException exception) {
      String msg = "";
      for (ConstraintViolation<?> cv : exception.getConstraintViolations()) {
          msg+=cv.getPropertyPath()+" "+cv.getMessage()+"\n";
      }
      return msg;
  }
}

A JAX-RS resource with constraint annotations

@Path("/customers")
public class CustomerResource {

  @POST
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  public String createCustomer(@NotNull @FormParam("name") String name,
                               @NotNull @FormParam("address") String address,
                               @NotNull @Pattern(regexp = "\\d{3}-\\d{3}-\\d{4}")
                               @FormParam("phone-number") String phoneNumber) {
      System.out.println("-- in createCustomer() method --");
      return String.format("created dummy customer name: %s, address: %s, phoneNumber:%s%n"
              , name, address, phoneNumber);

  }
}

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 {
      Form form = new Form();
      form.param("name", null)
          .param("address", null)
          .param("phone-number", "343-343-343");

      Client client = ClientBuilder.newBuilder().build();
      WebTarget target =
              client.target("http://localhost:8080/customers");

      Future<Response> future = target.request(MediaType.APPLICATION_FORM_URLENCODED)
                                      .buildPost(Entity.form(form))
                                      .submit(Response.class);
      Response r = future.get();
      if (r.getStatus() != Response.Status.OK.getStatusCode()) {
          System.out.println(r.getStatus() + " - " + r.getStatusInfo());
      }

      System.out.println(r.readEntity(String.class));
  }
}

Output

400 - Bad Request
createCustomer.arg0 may not be null
createCustomer.arg1 may not be null
createCustomer.arg2 must match "\d{3}-\d{3}-\d{4}"

Note that, in above JAX-RS client, we captured Response.class instead of directly reading message body via String.class. This is to avoid ExecutionException which wraps WebApplicationException or one of its subclasses (if the response status code is not successful). See the Invocation.submit() for details.

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

Mapping ConstraintViolationException Select All Download
  • jaxrs-bean-validation-exception-mapper
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also