Skip to content

Instantly share code, notes, and snippets.

@daniel-shuy
Last active June 22, 2025 09:30
Show Gist options
  • Save daniel-shuy/6ea3942ccd944800d1fd2f28683e79d2 to your computer and use it in GitHub Desktop.
Save daniel-shuy/6ea3942ccd944800d1fd2f28683e79d2 to your computer and use it in GitHub Desktop.
JAX-RS ExceptionMapper to handle Bean Validation's ConstraintViolationException
import com.google.common.collect.Iterables;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException exception) {
Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
List<String> errorMessages = constraintViolations.stream()
.map(constraintViolation -> {
String name = Iterables.getLast(constraintViolation.getPropertyPath()).getName();
return name + " " + constraintViolation.getMessage();
})
.collect(Collectors.toList());
return Response
.status(Response.Status.BAD_REQUEST)
.type(MediaType.APPLICATION_JSON_TYPE)
.entity(errorMessages)
.build();
}
}
@daniel-shuy
Copy link
Author

The ConstraintViolationExceptionMapper extracts the last node of the path to get the field name, then prepends it to the error message so that the @NotNull annotation doesn't need to specify the field name in the error message:

eg.

public class User {
    @NotNull
    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

instead of

public class User {
    @NotNull(message = "name {javax.validation.constraints.NotNull.message}")
    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The ConstraintViolationExceptionMapper combines all the error messages from all ConstraintViolation(s) into a List, which is then returned as a JSON array, together with a HTTP Status Code 400 (Bad Request).

This example uses Google Guava's Iterables#getLast(Iterable<T>) to get the last node of the path for brevity, it is not required otherwise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment