Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@Valid @RequestBody User user) {
    return new ResponseEntity<>(HttpStatus.OK);

Default 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"}

I would like to have my custom json for each error occured. How do I accomplish that?

If you want full control over the response message in every controller write a ControllerAdvice. For example, that example transform MethodArgumentNotValidException into a custom json object:

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;
                This answer is better since it uses AOP instead of putting validation checks in controller functions
– Varun Singh
                Nov 7, 2019 at 2:22

You can perform validation with Errors/BindingResult object. Add Errors argument to your controller method and customize the error message when errors found.

Below is the sample example, errors.hasErrors() returns true when validation is failed.

@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);
                @user6123723 The ApiError class is a customized class that has the data structure of the message that you want the client to receive, like for example a json with status, code, exception, error and timestamp, that's it.
– Leo
                Apr 19, 2018 at 14:16
                Still the approach isn't good enough. I found it more convenient to let an exception to be thrown and manage it in with ExceptionHandling like it's done in JHipster for instance(MethodArgumentNotValidException): github.com/jgasmi/jhipster/blob/master/src/main/java/com/…
– dbl
                Apr 19, 2018 at 19:25
                @vesa If you have a object with errors, Errors errors allows enter to your method for inspecting what errors have there been. Without this param, an exception will occur before and you will not able to enter to your method. In other words, with Errors errors you can analyze your errors programatically from inside your method.
– BeardOverflow
                May 6, 2021 at 5:30

I know this is kind of old question,

But I just run into it and I found some pretty good article which has also a perfect example in github.

Basically it uses @ControllerAdvice as Spring documentation suggests.

So for example catching 400 error will be achieved by overriding one function:

@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 class is a simple object to hold status, message, errors)

One way to do it is adding message in @NotNull annotation on entity properties. And adding @Valid annotation in controller request body.

public class User {
    @NotNull(message = "User name cannot be empty")
    private String name;
    @NotNull(message = "Password cannot be empty")
    private String password;

Controller:

@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*
                Specifying message can be used for other validations as well such as "@"pattern, "@"min, "@"max.  Sorry, I can't say @ + something because stackoverflow thinks I am trying to specify another stackoverflow user.
– cody.tv.weber
                Jul 11, 2019 at 19:28
@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());

You can do something like this

@ExceptionHandler(value = MethodArgumentNotValidException.class)
      protected ResponseEntity<Error> handleGlobalExceptions(MethodArgumentNotValidException ex,
          WebRequest request) {
        log.catching(ex);
        return new ResponseEntity<>(createErrorResp(HttpStatus.BAD_REQUEST,
            ex.getBindingResult().getFieldErrors().stream().map(err -> err.getDefaultMessage())
                .collect(java.util.stream.Collectors.joining(", "))),
            HttpStatus.BAD_REQUEST);

For customized the error message in JSON format then do the below steps.

- Create one @Component called CommonErrorHandler

@Component
public class CommonErrorHandler {
public  Map<String,Object> getFieldErrorResponse(BindingResult result){
        Map<String, Object> fielderror = new HashMap<>();
        List<FieldError>errors= result.getFieldErrors();
        for (FieldError error : errors) {
            fielderror.put(error.getField(), error.getDefaultMessage());
        }return fielderror;
     public ResponseEntity<Object> fieldErrorResponse(String message,Object fieldError){
        Map<String, Object> map = new HashMap<>();
        map.put("isSuccess", false);
        map.put("data", null);
        map.put("status", HttpStatus.BAD_REQUEST);
        map.put("message", message);
        map.put("timeStamp", DateUtils.getSysDate());
        map.put("filedError", fieldError);
        return new ResponseEntity<Object>(map,HttpStatus.BAD_REQUEST);

-- Add InvalidException class

public class InvalidDataException extends RuntimeException {
 * @author Ashok Parmar
    private static final long serialVersionUID = -4164793146536667139L;
    private BindingResult result;
    public InvalidDataException(BindingResult result) {
        super();
        this.setResult(result);
    public BindingResult getResult() {
        return result;
    public void setResult(BindingResult result) {
        this.result = result;

- Introduce @ControllerAdvice class

@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(InvalidDataException.class)
    public ResponseEntity<?> invalidDataException(InvalidDataException ex, WebRequest request) {
        List<FieldError> errors = ex.getResult().getFieldErrors();
        for (FieldError error : errors) {
            logger.error("Filed Name ::: " + error.getField() + "Error Message :::" + error.getDefaultMessage());
        return commonErrorHandler.fieldErrorResponse("Error", commonErrorHandler.getFieldErrorResponse(ex.getResult()));

-- Use in controller with @Valid and throw exception

public AnyBeans update(**@Valid** @RequestBody AnyBeans anyBeans ,
            BindingResult result) {
        AnyBeans resultStr = null;
        if (result.hasErrors()) {
            **throw new InvalidDataException(result);**
        } else {
                resultStr = anyBeansService.(anyBeans );
                return resultStr;

-- Output will be in JSON format

"timeStamp": 1590500231932, "data": null, "message": "Error", "isSuccess": false, "status": "BAD_REQUEST", "filedError": { "name": "Name is mandatory"

Hope this will be work. :-D

@ControllerAdvice
@RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
        @Override
        protected ResponseEntity<Object> handleMethodArgumentNotValid(
                MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
           // ex.getBindingResult(): extract the bind result for default message. 
              String errorResult = ex.getBindingResult().toString();
             CustomizedExceptionHandlerResponse exceptionResponse = new CustomizedExceptionHandlerResponse(
                    errorResult, new Date(), request.getDescription(false));
            return new ResponseEntity<>(exceptionResponse, HttpStatus.BAD_REQUEST);
class CustomizedExceptionHandlerResponse {
   private String message;
   private String status;
   private Date timestamp;
   // constuctor, setters, getters...

you can use this code to iterate through errors and build a custom error message :

import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.validation.ConstraintViolation;
import java.util.List;
import java.util.stream.Collectors;
@ControllerAdvice
public class ExceptionHandlerController {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorDto> handleException(MethodArgumentNotValidException ex) {
        ErrorDto dto = new ErrorDto(HttpStatus.BAD_REQUEST, "Validation error");
        dto.setDetailedMessages(ex.getBindingResult().getAllErrors().stream()
            .map(err -> err.unwrap(ConstraintViolation.class))
            .map(err -> String.format("'%s' %s", err.getPropertyPath(), err.getMessage()))
            .collect(Collectors.toList()));
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(dto);
    @Data
    public static class ErrorDto {
        private final int status;
        private final String error;
        private final String message;
        private List<String> detailedMessages;
        public ErrorDto(HttpStatus httpStatus, String message) {
            status = httpStatus.value();
            error = httpStatus.getReasonPhrase();
            this.message = message;

This will give you a response like this in case of error :

"status": 400, "error": "Bad Request", "message": "Validation error", "detailedMessages": [ "'yourField' should not be empty."

Add some information too. If you use just @Valid, you need to catch BindException. If you use @Valid @RequestBody catch MethodArgumentNotValidException

Some sources:
HandlerMethodArgumentResolverComposite.getArgumentResolver(MethodParameter parameter):129 - search which HandlerMethodArgumentResolver support such parameter RequestResponseBodyMethodProcessor.supportsParameter(MethodParameter parameter) - return true if parameter has annotation @RequestBody

RequestResponseBodyMethodProcessor:139 - throw MethodArgumentNotValidException ModelAttributeMethodProcessor:164 - throw BindException

I know this is an old question, but i stumbled across and decided to show a cleaner way of doing what Ksokol showed.

StandardError class:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StandardError {
    private Instant timestamp;
    private Integer status;
    private String error;
    private String message;
    private String path;

ControllerExceptionHandler class:

@ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<StandardError> notValid(MethodArgumentNotValidException e, HttpServletRequest request) {
        String error = "Dados inválidos.";
        HttpStatus status = HttpStatus.UNPROCESSABLE_ENTITY;
        StandardError err = new StandardError(
                Instant.now(),
                status.value(),
                error,
                e.getBindingResult()
                        .getFieldErrors()
                        .stream()
                        .map(FieldError::getDefaultMessage)
                        .collect(Collectors.toSet())
                        .toString()
                        .replaceAll("\\[*]*", ""),
                request.getRequestURI()
        return ResponseEntity.status(status).body(err);

This way if you have a custom message on your bean validator, it will appear formatted in a error.

Result:

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.