PAA
PAA

Reputation: 11985

Spring Boot MVC - Do we need to use @Valid with BindingResult?

I am using Spring Boot + Spring Data JPA with Binding Results example. I have almost 100 controllers and for each Create/Update Request, we are doing below.

@PostMapping
public ResponseEntity<HttpStatus> saveStudent(@Valid @RequestBody StudentDto dto, BindingResult br){
    if (br.hasErrors()) {
        throw new InvalidRequestException("Wrong resource", br);
    }

    divisionService.saveStudent(dto);
    return new ResponseEntity<>(HttpStatus.CREATED);
}

Is there any way if we can centralized this logic in one place ? Can we develop listeners for the same ?

if (br.hasErrors()) {
    throw new InvalidRequestException("Wrong resource", br);
}

Upvotes: 2

Views: 2782

Answers (1)

Alan Hay
Alan Hay

Reputation: 23226

You don't need a reference to the BindingResult in your controller.

You would only need a reference to BindingResult in the controller if you wanted to base some custom logic on whether or not it had errors e.g. redirect to a different view in a Rest API scenario.

Validation exceptions will be thrown either by the Java bean Validation framework (via you @Valid annotation) or by any Spring Validator implementations applicable for the request.

In the case of the latter a MethodArgumentNotValidException will br thrown by the framework so you can simply create a ControllerAdvice (https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc) implementation which handles exceptions of this type and the BindingResult can be fetched from this.

@ControllerAdvice
public class ValidationErrorHandlingAdvice {

    /**
     * Handler for {@link MethodArgumentNotValidException}s. These are triggered by
     * Spring {@link Validator} implementations being invoked by the Spring MVC
     * controllers and returning validation errors.
     * 
     * 
     * @param ex The {@link MethodArgumentNotValidException} to be handled.
     * 
     * @return {@link Map} keyed by field to which the error is bound and with the
     *         value of the field as a value.
     */
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public @ResponseBody ValidationErrors handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        Map<String, List<String>> errorMap = new TreeMap<>();

        BindingResult result = ex.getBindingResult();
        for (FieldError error : result.getFieldErrors()) {

            if (!errorMap.containsKey(error.getField())) {
                errorMap.put(error.getField(), new ArrayList<String>());
            }

            errorMap.get(error.getField()).add(error.getDefaultMessage());
        }

        return new ValidationErrors(errorMap);
    }


    private static class ValidationErrors {
        private Map<String, List<String>> errors;

        public ValidationErrors(Map<String, List<String>> errors) {
            this.errors = errors;
        }

        @SuppressWarnings("unused")
        public Map<String, List<String>> getErrors() {
            return errors;
        }
    }
}

Upvotes: 1

Related Questions