«Проверка не удалась для object =’user’. Количество ошибок: 1» вместо «адрес электронной почты не должен быть нулевым»

#java #spring #hibernate #rest #validation

#java #весна #впасть в спящий режим #отдых #проверка

Вопрос:

У меня есть API, который проверяет и создает пользователя. Когда я не ввожу адрес электронной почты в теле запроса, я получаю

 {
    "timestamp": "2020-11-26T13:55:40.116 00:00",
    "status": 400,
    "error": "Bad Request",
    "message": "Validation failed for object='user'. Error count: 1",
    "path": "/api/users"
}
 

Я хочу изменить сообщение на более понятную ошибку типа «адрес электронной почты должен быть не нулевым».
Вот как выглядит свойство моей модели:

 @NotNull
private String email;
 

А вот и метод моего контроллера:

 @PostMapping("/users")
public ResponseEntity<User> addUser(@RequestBody @Valid User user) {
    User savedUser = manager.save(user);
    URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(savedUser.getId())
            .toUri();
    return ResponseEntity.ok(savedUser);
}
 

Я пытался установить пользовательское сообщение в @NotNull аннотации, но сообщение не меняется. Также, когда выдается ошибка, консоль показывает это сообщение:

 2020-11-26 15:09:11.460  WARN 7137 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity<pl.vemu.socialApp.entities.User> pl.vemu.socialApp.controllers.UserController.addUser(pl.vemu.socialApp.entities.User) throws pl.vemu.socialApp.exceptions.user.UserWithEmailAlreadyExistException: [Field error in object 'user' on field 'email': rejected value [xsars.pl]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@2a50b33,.*]; default message [must be a well-formed email address]] ]
 

Как установить сообщение в ответ в формате json?
Как избавиться от этого сообщения в консоли? Другие ошибки не отображают сообщение в консоли.

Ответ №1:

Проблема в том, что Spring по умолчанию не отправляет ошибки при привязке. Чтобы включить его, вы должны добавить server.error.include-binding-errors=always в свой application.properties.

Ответ №2:

Вместо того чтобы полагаться на "message" свойство, вы можете использовать "defaultMessage" свойство.

Вы можете установить пользовательское сообщение об ошибке @NotNull в аннотации.

 @NotNull(message = "Name cannot be null")
private String name;

@NotNull(message = "email cannot be null")
@NotEmpty(message = "email cannot be empty")
private String email;
 

Если проверка не удалась для поля, spring эти пользовательские сообщения будут установлены в "defaultMessage" свойстве

Как вы сказали в комментариях, что не смогли найти DefaultMessage, другим подходом (возможно, грязным подходом) было бы вручную создать тело ответа, вы можете сослаться на следующий пример кода для его создания:

 @PostMapping("saveEmployee")
public ResponseEntity<?> eat(@RequestBody @Valid Employee employee, Errors errors) {
    if (errors.hasFieldErrors()) {
        FieldError fieldError = errors.getFieldError();
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(fieldError.getDefaultMessage());
    }
    return ResponseEntity.ok(employee);
}
 

FieldError#getDefaultMessage() отобразит значение message атрибута в @NotNull(message = "email can't be null")

Комментарии:

1. Как вернуть DefaultMessage в json по запросу?

2. spring по умолчанию включит его, если проверка завершится неудачей.

3. Хм, в моем случае это не так

4. @Vemu тогда, возможно, вам следует создать ответ вручную. проверьте обновленный ответ

5. это работает, но также связывает логику проверки с реализацией контроллера.

Ответ №3:

 ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body("email must be not null");
 

и HttpStatus.UNPROCESSABLE_ENTITY замените подходящим кодом ошибки.

Комментарии:

1. Я хочу, чтобы валидатор spring обработал проверку модели

Ответ №4:

используйте также @Validated на уровне класса плюс используйте пользовательский обработчик ошибок, подобный этому

 @Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {

    String violations = e.getConstraintViolations().stream().map(cv -> cv.getConstraintDescriptor().getMessageTemplate()).toList().toString();

    return new ResponseEntity<>
            ("not valid due to validation errors: "   violations, HttpStatus.BAD_REQUEST);
}
 

}

Ответ №5:

Вы можете переопределить сообщения об ошибках по умолчанию в BindException.

 @ControllerAdvice
public class BindExceptionHandler {
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public String handleBindExceptionHandler(BindException exception) {
        return "Your message like 'email must be not null'";
    }
}