In Spring 4.x, if you use a @RestControllerAdvise
(or @ControllerAdvice
) that extends ResponseEntityExceptionHandler
, the default exception handling with nice and informative JSON response bodies, is no longer returned by default for arguments marked as @Valid
.
How do you get the default JSON bodies to be returned while using a ResponseEntityExceptionHandler
based @RestControllerAdvice
?
The following is a simple, yet full example describing this question. Using these classes:
@RestController
class CarsController {
@PostMapping("/cars")
public void createCar(@RequestBody @Valid Car car) {
System.out.println("Creating " + car);
throw new WhateverException();
}
@ExceptionHandler(WhateverException.class)
@ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public void handleWhateverException(){
System.out.println("Handling a WhateverException.");
}
}
class Car {
@NotNull
private String make;
@NotNull
private String model;
...getter/setters omitted for brevity...
}
class WhateverException extends RuntimeException {}
If you submit a POST
to /cars
with
{
"make": "BMW"
}
It responds with a 400
and the following body:
{
"timestamp": 1491020374642,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.MethodArgumentNotValidException",
"errors": [
{
"codes": [
"NotNull.car.model",
"NotNull.model",
"NotNull.java.lang.String",
"NotNull"
],
"arguments": [
{
"codes": [
"car.model",
"model"
],
"arguments": null,
"defaultMessage": "model",
"code": "model"
}
],
"defaultMessage": "may not be null",
"objectName": "car",
"field": "model",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
}
],
"message": "Validation failed for object='car'. Error count: 1",
"path": "/cars"
}
However if you move the exception handling method to it's own class marked @RestControllerAdvice
, which extends from ResponseEntityExceptionHandler
such as the following:
@RestControllerAdvice
class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(WhateverException.class)
@ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public void handleWhateverException(WhateverException e, HttpServletRequest httpServletRequest) {
System.out.println("Handling a WhateverException.");
}
}
You'll get a 400
with an empty body, which is caused by ResponseEntityExceptionHandler
providing a method (handleMethodArgumentNotValid(..)
), which builds a response where the body is null
.
How would you alter this @RestControllerAdvice
class to trigger the original handling that occurs, which provides a JSON body describing why the submitted request is invalid?
How about this?
@RestControllerAdvice
class RestExceptionHandler {
@ExceptionHandler(org.springframework.validation.BindException.class)
public ResponseEntity<String> handleBindException(org.springframework.validation.BindException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
when @Valid
fails, it throws BindException. Which you can handle it like this. or you can just do throw e
which would give you the exact same response as was thrown earlier
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With