31

When the PathVariable 'name' doesn't pass validation a javax.validation.ConstraintViolationException is thrown. Is there a way to retrieve the parameter name in the thrown javax.validation.ConstraintViolationException?

@RestController
@Validated
public class HelloController {

@RequestMapping("/hi/{name}")
public String sayHi(@Size(max = 10, min = 3, message = "name should    have between 3 and 10 characters") @PathVariable("name") String name) {
  return "Hi " + name;
}
Josh
  • 371
  • 1
  • 4
  • 12

8 Answers8

26

If you inspect the return value of getPropertyPath(), you'll find it'a Iterable<Node> and the last element of the iterator is the field name. The following code works for me:

// I only need the first violation
ConstraintViolation<?> violation = ex.getConstraintViolations().iterator().next();
// get the last node of the violation
String field = null;
for (Node node : violation.getPropertyPath()) {
    field = node.getName();
}
leowang
  • 481
  • 5
  • 12
20

The following Exception Handler shows how it works :

@ExceptionHandler(ConstraintViolationException.class)

ResponseEntity<Set<String>> handleConstraintViolation(ConstraintViolationException e) {
    Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();

Set<String> messages = new HashSet<>(constraintViolations.size());
messages.addAll(constraintViolations.stream()
        .map(constraintViolation -> String.format("%s value '%s' %s", constraintViolation.getPropertyPath(),
                constraintViolation.getInvalidValue(), constraintViolation.getMessage()))
        .collect(Collectors.toList()));

return new ResponseEntity<>(messages, HttpStatus.BAD_REQUEST);

}

You can access the invalid value (name) with

 constraintViolation.getInvalidValue()

You can access the property name 'name' with

constraintViolation.getPropertyPath()
jaysingkar
  • 4,315
  • 1
  • 18
  • 26
10

use this method(ex is ConstraintViolationException instance):

Set<ConstraintViolation<?>> set =  ex.getConstraintViolations();
    List<ErrorField> errorFields = new ArrayList<>(set.size());
    ErrorField field = null;
    for (Iterator<ConstraintViolation<?>> iterator = set.iterator();iterator.hasNext(); ) {
        ConstraintViolation<?> next =  iterator.next();
       System.out.println(((PathImpl)next.getPropertyPath())
                .getLeafNode().getName() + "  " +next.getMessage());


    }
Stephen.lin
  • 166
  • 1
  • 12
6

To get only parameter name which is last part of the Path.

violations.stream()
                .map(violation -> String.format("%s value '%s' %s", StreamSupport.stream(violation.getPropertyPath().spliterator(), false).reduce((first, second) -> second).orElse(null),
                        violation.getInvalidValue(), violation.getMessage())).collect(Collectors.toList());
ravthiru
  • 8,878
  • 2
  • 43
  • 52
2

I had the same problem but also got "sayHi.arg0" from getPropertyPath. I chose to add a message to NotNull annotations since they were part of our public API. Like:

 @NotNull(message = "timezone param is mandatory")

you can obtain the message by calling

ConstraintViolation#getMessage()

Pepster
  • 1,996
  • 1
  • 24
  • 41
2

To get field name and message, used below code:

@ExceptionHandler(value = {ConstraintViolationException.class})
public ResponseEntity<Object> handleConstraintViolationException(
    ConstraintViolationException ex, WebRequest request) {
    log.error(INVALID_REQUEST, ex);
    Map<String, Object> errors = new HashMap<>();
    if (!ex.getConstraintViolations().isEmpty()) {
        for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) {
            String fieldName = null;
            for (Node node : constraintViolation.getPropertyPath()) {
                fieldName = node.getName();
            }
            errors.put(fieldName, constraintViolation.getMessage());
        }
    }
    return new ResponseEntity<>(getErrorResponse(code, errors), getHttpCode());
}
Awadesh
  • 3,530
  • 2
  • 20
  • 32
0

In ControllerAdvice class, we can handle ConstraintViolationException

import javax.validation.Path;
import javax.validation.Path.Node;
import org.hibernate.validator.internal.engine.path.PathImpl;

....
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<FieldErrorResponse> handleConstraintViolationException(final ConstraintViolationException ex, WebRequest request) {

    List<FieldError> errors = ex.getConstraintViolations().stream()
      .map(violation -> new FieldError(getFieldFromPath(violation.getPropertyPath()), violation.getMessage(), violation.getInvalidValue()))
      .collect(Collectors.toList());
    ....
}

private String getFieldFromPath(Path fieldPath) {

    PathImpl pathImpl = (PathImpl) fieldPath;
    return pathImpl.getLeafNode().toString();

    // OR
    /**
    Iterator<Node> nodes = fieldPath.iterator();
    String fieldName = null;
    while (nodes.hasNext()) {
      fieldName = nodes.next().toString();
    }
    return fieldName;
    */
}

Two approaches are there to get field names. First, one uses hibernate class. If you don't have you can go with the commented approach.

Satish Patro
  • 3,645
  • 2
  • 27
  • 53
0

Please find below code.

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public Map<String,String> handleValidationException(ConstraintViolationException ex){
    Map<String,String> errorMap = new HashMap<>();
    ex.getConstraintViolations().forEach(error -> {
        errorMap.put(error.getPropertyPath().toString(), error.getMessage());
    });
    return errorMap;
}

}

  • 1
    Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? **If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient.** Can you kindly [edit] your answer to offer an explanation? – Jeremy Caney Aug 19 '23 at 00:24