Как настроить сообщение об ошибке по умолчанию из spring @Valid validation?
DTO:
public class User {
@NotNull
private String name;
@NotNull
private String password;
//..
}
Контроллер:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@Valid @RequestBody User user) {
//..
return new ResponseEntity<>(HttpStatus.OK);
}
По умолчанию json error:
{"timestamp":1417379464584,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.MethodArgumentNotValidException","message":"Validation failed for argument at index 0 in method: public org.springframework.http.ResponseEntity<demo.User> demo.UserController.saveUser(demo.User), with 2 error(s): [Field error in object 'user' on field 'name': rejected value [null]; codes [NotNull.user.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [may not be null]],"path":"/user"}
Я хотел бы иметь свой пользовательский json для каждой ошибки. Как это сделать?
Ответы
Ответ 1
Вы можете выполнить проверку с помощью объекта Errors/BindingResult.
Добавьте аргумент Errors в свой метод контроллера и настройте сообщение об ошибке при обнаружении ошибок.
Ниже приведен пример примера errors.hasErrors() возвращает true, если проверка не выполнена.
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<String> saveUser(@Valid @RequestBody User user, Errors errors) {
if (errors.hasErrors()) {
return new ResponseEntity(new ApiErrors(errors), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
Ответ 2
Если вам нужен полный контроль над ответным сообщением в каждом контроллере, напишите ControllerAdvice
. Например, этот пример преобразует MethodArgumentNotValidException
в пользовательский объект json:
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
/**
* Kudos http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/
*
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class MethodArgumentNotValidExceptionHandler {
@ResponseStatus(BAD_REQUEST)
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public Error methodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<org.springframework.validation.FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
private Error processFieldErrors(List<org.springframework.validation.FieldError> fieldErrors) {
Error error = new Error(BAD_REQUEST.value(), "validation error");
for (org.springframework.validation.FieldError fieldError: fieldErrors) {
error.addFieldError(fieldError.getField(), fieldError.getDefaultMessage());
}
return error;
}
static class Error {
private final int status;
private final String message;
private List<FieldError> fieldErrors = new ArrayList<>();
Error(int status, String message) {
this.status = status;
this.message = message;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
public void addFieldError(String path, String message) {
FieldError error = new FieldError(path, message);
fieldErrors.add(error);
}
public List<FieldError> getFieldErrors() {
return fieldErrors;
}
}
}
Ответ 3
Я знаю, что это старый вопрос,
Но я просто наткнулся на это и нашел довольно неплохую статью, в которой также есть отличный пример на github.
В основном он использует @ControllerAdvice
как предполагает документация Spring.
Так, например, обнаружение ошибки 400 будет достигнуто путем переопределения одной функции:
@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
logger.info(ex.getClass().getName());
//
final List<String> errors = new ArrayList<String>();
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.add(error.getField() + ": " + error.getDefaultMessage());
}
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
}
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
}
}
(Класс ApiError - это простой объект для хранения статуса, сообщения, ошибок)
Ответ 4
Один из способов сделать это - добавить сообщение в аннотацию @NotNull.
DTO:
public class User {
@NotNull(message = "User name cannot be empty")
private String name;
@NotNull(message = "Password cannot be empty")
private String password;
//..
}
Контроллер:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@Valid @RequestBody User user) {
//..
return new ResponseEntity<>(HttpStatus.OK);
}
// Add one
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<List<YourErrorResponse>> handleException(MethodArgumentNotValidException ex) {
// Loop through FieldErrors in ex.getBindingResult();
// return *YourErrorReponse* filled using *fieldErrors*
}
Ответ 5
@ControllerAdvice(annotations = RestController.class)
public class GlobalExceptionHandler implements ApplicationContextAware {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public ApplicationError validationException(MethodArgumentNotValidException e) {
e.printStackTrace();
return new ApplicationError(SysMessageEnum.MSG_005, e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}
}
Ответ 6
{
"message": "string",
"errors": [
{
"field": "string",
"message": "string",
"error_code": "string"
}
],
"resource": "string",
"description": "string"
}