What type of exception to throw in Spring RestController when validation fails?

Gregor picture Gregor · Mar 3, 2016 · Viewed 23.4k times · Source

In a Spring RestController I have an input validation of the RequestBody simply by annotating the corresponding method parameter as @Valid or @Validated. Some other validations can only be performed after some processing of the incoming data. My question is, what type of exceptions should I use, so that it resembles the exception thrown by the @Valid annotation, and how do I construct this exception from the validation result. Here is an example:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createOrder(@RequestBody @Validated(InputChecks.class) Order order) {
    // Some processing of the Order goes here
    Set<ConstraintViolation<Order>> violations = validator.validate(order, FinalChecks.class);
    // What to do now with the validation errors?
    orders.put(order);
    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().path("/" + order.getId()).build().toUri());
    return new ResponseEntity<>(null, headers, HttpStatus.CREATED);
}

Answer

Gregor picture Gregor · Mar 4, 2016

To me the simplest way looks like validating the object with an errors object, and use it in a MethodArgumentNotValidException.

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createOrder(@RequestBody @Validated(InputChecks.class) Order order)
                throws NoSuchMethodException, SecurityException, MethodArgumentNotValidException {
    // Some processing of the Order goes here
    SpringValidatorAdapter v = new SpringValidatorAdapter(validator);
    BeanPropertyBindingResult errors = new BeanPropertyBindingResult(order, "order");
    v.validate(order, errors, FinalChecks.class);
    if (errors.hasErrors()) {
        throw new MethodArgumentNotValidException(
                new MethodParameter(this.getClass().getDeclaredMethod("createOrder", Order.class), 0),
                errors);
    }
    orders.put(order);
    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(ServletUriComponentsBuilder.fromCurrentRequest().path("/" + order.getId()).build().toUri());
    return new ResponseEntity<>(null, headers, HttpStatus.CREATED);
}

This way the errors found during the second validation step have exactly the same structure as the errors found during the input validation on the @validated parameters.