spring-boot: @PathVariable validation gives 500 instead of 400

@RestController
@Validated
class DemoController {

	@GetMapping("/{message}")
	String fails(@PathVariable("message") @Size(min = 3) String message) {
		return "Message is " + message;
	}

	@PostMapping
	String works(@Validated @RequestBody RequestDto message) {
		return "Message is " + message.getMessage();
	}
}

class RequestDto {//getters setters omitted
	@Size(min = 3)
	private String message;
}

When validating @PathVariables as shown above it gives 500 instead of 400. I think this is both wrong and unintuitive because this is a similar case when we validate @RequestBodys which give 400 when validation fails.

response of fails method when validation fails:

{
    "timestamp": 1506928557130,
    "status": 500,
    "error": "Internal Server Error",
    "message": "No message available",
    "path": "/a"
}

response of works method when validation fails:

{
    "timestamp": 1506928056306,
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "Size.requestDto.message",
                "Size.message",
                "Size"
            ],
            "arguments": [
                {
                    "codes": [
                        "requestDto.message",
                        "message"
                    ],
                    "arguments": null,
                    "defaultMessage": "message",
                    "code": "message"
                },
                2147483647,
                3
            ],
            "defaultMessage": "size must be between 3 and 2147483647",
            "objectName": "requestDto",
            "field": "message",
            "rejectedValue": "a",
            "bindingFailure": false,
            "code": "Size"
        }
    ],
    "message": "Validation failed for object='requestDto'. Error count: 1",
    "path": "/"
}

is there any plan to fix this or is this state considered normal?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 15 (7 by maintainers)

Most upvoted comments

Solution which worked for me in SpringBoot 2.1.1.RELEASE was to add an additional ExceptionHandler to the ControllerAdvice class as follows:

 @ControllerAdvice
public class ControllerExceptionHandler {

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ValidationException.class)
    ErrorMessage exceptionHandler(ValidationException e) {
        return new ErrorMessage("400", e.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    ErrorMessage exceptionHandler(ConstraintViolationException e) {
        return new ErrorMessage("400", e.getMessage());
    }

}

I’ll close this one here since there’s not much we can do in Spring Boot. @destan If you raise a framework issue would you be so kind as to paste link here.

@Laharnaman Annotate the class with RestControllerAdvice and you can get rid of the @ResponseBody annotations 😃.

Actually, it looks like re-throwing the exception from within an overridden ResponseEntityExceptionHandler#handleBindException() allows Spring Boot to handle the exception. I get back a properly formatted JSON response body with the full "errors" array describing the binding problem. But you have to use something like Lombok’s @SneakyThrows to do it, because BindException is checked 😦

@ControllerAdvice
public class GlobalExceptionControllerAdviceHandler extends ResponseEntityExceptionHandler {
    @SneakyThrows
    @Override
    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,
                                                         WebRequest request) {
        // Allow Spring Boot to handle it
        throw ex;
    }
}