Spring Boot MVC — нужно ли нам использовать @Valid с BindingResult?

Я использую Spring Boot + Spring Data JPA с примером результатов привязки. У меня почти 100 контроллеров и для каждого запроса на создание/обновление мы делаем ниже.

@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);
}

Есть ли способ, если мы можем централизовать эту логику в одном месте? Можем ли мы разработать слушателей для того же самого?

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

2
1 229
1

Ответ:

Решено

Вам не нужна ссылка на BindingResult в вашем контроллере.

Вам понадобится только ссылка на BindingResult в контроллере, если вы хотите основывать некоторую пользовательскую логику на том, есть ли у нее ошибки, например. перенаправить на другое представление в сценарии Rest API.

Исключения проверки будут генерироваться либо инфраструктурой проверки Java-бина (через вы @Valid аннотацию) или любыми реализациями Spring Validator, применимыми к запросу.

В последнем случае фреймворк вызовет исключение MethodArgumentNotValidException, поэтому вы можете просто создать реализацию ControllerAdvice (https://spring.io/blog/2013/11/01/обработка-исключений-в-весне-mvc), которая обрабатывает исключения этого типа, и из этого можно получить BindingResult.

@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;
        }
    }
}