9

Bean Validation is a good option to validate objects, but how to customize the response of a REST API (using RESTeasy) when a ConstraintViolationException is thrown?

For example:

@POST
@Path("company")
@Consumes("application/json")
public void saveCompany(@Valid Company company) {
    ...
}

A request with invalid data will return a HTTP 400 status code with the following body:

[PARAMETER]
[saveCompany.arg0.name]
[{company.name.size}]
[a]

It's nice but not enough, I would like to normalize these kind of errors in a JSON document.

How can I customize this behavior?

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
Francois
  • 93
  • 1
  • 5

1 Answers1

16

With JAX-RS can define an ExceptionMapper to handle ConstraintViolationExceptions.

From the ConstraintViolationException, you can get a set of ConstraintViolation, that exposes the constraint violation context, then map the details you need to an abitrary class and return in the response:

@Provider
public class ConstraintViolationExceptionMapper 
       implements ExceptionMapper<ConstraintViolationException> {

    @Override
    public Response toResponse(ConstraintViolationException exception) {

        List<ValidationError> errors = exception.getConstraintViolations().stream()
                .map(this::toValidationError)
                .collect(Collectors.toList());

        return Response.status(Response.Status.BAD_REQUEST).entity(errors)
                       .type(MediaType.APPLICATION_JSON).build();
    }

    private ValidationError toValidationError(ConstraintViolation constraintViolation) {
        ValidationError error = new ValidationError();
        error.setPath(constraintViolation.getPropertyPath().toString());
        error.setMessage(constraintViolation.getMessage());
        return error;
    }
}
public class ValidationError {

    private String path;
    private String message;

    // Getters and setters
}

If you use Jackson for JSON parsing, you may want to have a look at this answer, showing how to get the value of the actual JSON property.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • Thanks, but the provider seem to be ignored ! (resteasy 3.0.19.Final, wildfly 10.1.0.final), any tips ? – Francois Jun 01 '17 at 13:57
  • @Francois What is you application like? Do you have a `web.xml`? Do you extend `Application`? More details are welcome to help you. – cassiomolin Jun 01 '17 at 14:01
  • yes i have a web.xml but it s empty, and i have a classic rest api app @ApplicationPath("/") public class ApplicationConfig extends Application { @SuppressWarnings("unchecked") public Set> getClasses() { return new HashSet>(Arrays.asList(CustomerService.class)); } } – Francois Jun 01 '17 at 14:03
  • @Francois You are overriding the `getClasses()` method, hence the auto-scanning won't pick the provider automatically. Register also the provider: `return new HashSet<>(Arrays.asList(CustomerService.class, ConstraintViolationExceptionMapper.class));` – cassiomolin Jun 01 '17 at 14:06
  • My Mapper that implements ExceptionMapper does not pick up the Exception and I get the same result as the OP. What am I missing ? (Resteasy, Wildfy11 RC) – Tim Oct 17 '17 at 12:17
  • 1
    great simple construction! Good job – Max Mazur Jun 04 '19 at 11:38