Validation response is missing errors details

Viewed 364

I have a Spring Boot Rest Controller, which receives a post body with a validated field. If I send a body that that violates the validation, I get an error response.

But in that response I am missing the detailed description of the errors, which part of the validation is failing. In the logged Exception in the Backend this is included, but I would like to have is visible for the client:

HTTP Response:

{
  "timestamp": "2020-11-19T12:15:34.957+00:00",
  "status": 400,
  "error": "Bad Request",
  "message": "Validation failed for object='postBody'. Error count: 1",
  // <-- here I am missing the errors field that contains the (list of) error messages.
  "path": "/comments"
}

Controller and PostBody:

@RestController
@Validated
public class CommentsController {

    public void createComment(PostBody postBody) {
      //do stuff
    }
}

public class PostBody {
    private String text;

    @Size(max = 10) 
    public String getText() {
        return text;
    }
    // setter
}

Exception in the backend logs contains the errors:

2020-11-19 13:15:34 WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - 
    Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in 
    public CommentsController.createComment(PostBody): 
    [Field error in object 'postBody' on field 'text': rejected value [my very long input]; 
    codes [Size.postBody.text,Size.text,Size.java.lang.String,Size]; arguments 
    [org.springframework.context.support.DefaultMessageSourceResolvable: codes [postBody.text,text]; 
    arguments []; default message [text],10,0]; default message [size must be between 0 and 10]] ]

Do I need to configure anything additional to get the errors details into the response? I would expect this to be included out of the box.

2 Answers

I have to add server.error.include-binding-errors: always in my application.yml in order to enable the "errors" field in the error resonse:

So my application.yml is now:

server:
  error:
    include-message: always
    include-binding-errors: always

And the Response looks like the following. This is more detailed than I expected, but it contains the required information without the need to write code, just configuration:

{
  "timestamp": "2020-11-19T12:15:34.957+00:00",
  "status": 400,
  "errors": [
    {
      "codes": [
        "Size.postBody.text",
        "Size.text",
        "Size.java.lang.String",
        "Size"
      ],
      "arguments": [
        {
          "codes": [
            "postBody.text",
            "text"
          ],
          "arguments": null,
          "defaultMessage": "text",
          "code": "text"
        },
        10,
        0
      ],
      "defaultMessage": "size must be between 0 and 10",
      "objectName": "postBody",
      "field": "text",
      "rejectedValue": "my very long input",
      "bindingFailure": false,
      "code": "Size"
    }
  "error": "Bad Request",
  "message": "Validation failed for object='postBody'. Error count: 1", messages.
  "path": "/comments"
}

In the application.yml I already configured the field server.error.include-message: always. Otherwise the "message": value in the error response would just be an empty String.

And here is the GitHub discussion about it: https://github.com/spring-projects/spring-boot/issues/20505#issuecomment-621295137

I recommend you to use a method to check about your entity validation, there is an example

 private void checkIfReadyToSave(PostBody myPost) {
    Set<ConstraintViolation<Product>> violations =
        validator.validate(product, PostBodyCreation.class);
    if (!violations.isEmpty()) {
      Map<String, Object> body = new LinkedHashMap<>();
      Set<String> keys =
          violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.toSet());
      body.put("errors", keys);
      throw new BeanValidationException(ErrorConstants.POST_BODY_MISSING_ERROR_MSG, body.toString());
    }
  }

And this how you can make your model validation by using spring validation

public class PostBody {



@NotBlank(groups = PostBodyCreation.class, message = "text.notSetted")
@Field("text")
private String text;

And this how to make the validation interface :

import javax.validation.groups.Default;

public interface PostBodyCreation extends Default {
}
Related