Issue
I Need to get something like this in the Controller of my app, in create user method which has different validations one for password, another for email, name and more. I already done the part of "detail" by parsing the BindingResult getDefaultMessage, but i need to check for the type of exception to parse it to corresponding http code, how could i do it?
{
"mistakes": [
{
"status": "403",
"detail": "Password should have 16 characters."
},
{
"status": "422",
"detail": "Invalid Email Format."
},
{
"status": "500",
"detail": "Name cannot be empty."
}
]
}
Solution
You can't catch multiple exceptions, but you can write common handling logic for different exceptions, using Spring's @ExceptionHandler
.
As you want to respond with some JSON from your controller (not with a view) I assume you're developing a REST API.
Then, first of all, you should think about returning 400 (Bad Request) status code in your scenario as you're validating user input and it's a best way to show the client that data from him doesn't follow your API rules (violates API specs). So I think in all those cases you mentioned it would be wise to respond with 400.
If you use javax.validation for your method arguments of your controller, BindException.class
does all the magic for you, cumulating all validation errors in one place. So basically you can catch this exception and transform it into some custom error DTO.
For example, like this:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDTO handleValidationError(BindException ex) {
return new ErrorDTO(bindErrorsToList(ex));
}
private List<ErrorDetail > bindErrorsToList(BindException ex) {
List<ErrorDetail> validationErrorDetails = new LinkedHashMap<>();
if (ex.hasFieldErrors()) {
ex.getFieldErrors().stream()
.map(this::fieldErrorToDetails)
.collect(Collectors.toList()));
}
return validationErrorDetails;
}
private ErrorDetail fieldErrorToDetails(FieldError error) {
return new ErrorDetail(error.getField(), error.getRejectedValue(), error.getDefaultMessage());
}
}
public class ErrorDTO {
private List<ErrorDetail> mistakes;
// constructors, getters, setters...
}
public class ErrorDetail {
private String field;
private String rejectedValue;
private String detail;
// constructors, getters, setters...
}
This code will return something like this after failed validation of DTO passed to your controller (along with 400 status code):
{
"mistakes": [
{
"field": "password",
"rejected_value": "some_pass",
"detail": "Password should have 16 characters."
},
{
"field": "email",
"rejected_value": "test@g",
"detail": "Invalid Email Format."
}
]
}
Answered By - AndrewThomas
Answer Checked By - Clifford M. (JavaFixing Volunteer)