Handling Validation Errors Gracefully in Spring Boot with BindingResult (Without Throwing Exceptions)
Validation is one of the most important aspects of building secure and reliable REST APIs. Spring Boot makes validation simple with Bean Validation annotations like @NotNull, @Email, and…
Validation is one of the most important aspects of building secure and reliable REST APIs. Spring Boot makes validation simple with Bean Validation annotations like @NotNull, @Email, and @Size. However, many developers rely on exceptions to handle validation failures, which isn’t always the best approach.
In this article, we’ll explore how to use BindingResult to handle validation errors gracefully without throwing exceptions.
What is BindingResult?
BindingResult is an interface in Spring Framework that stores the results of data binding and validation.
When used immediately after a parameter annotated with @Valid or @Validated, it captures validation errors instead of allowing Spring to throw a MethodArgumentNotValidException.
This gives you complete control over how validation errors are returned to the client.
Why Use BindingResult?
By default, Spring Boot throws an exception when validation fails.
For example:
@PostMapping("/users")
public ResponseEntity<String> createUser(@Valid @RequestBody UserDto dto) {
return ResponseEntity.ok("User Created");
}
If any validation constraint fails, Spring automatically throws:
MethodArgumentNotValidException
Although this works, sometimes you may want to:
- Return custom error messages
- Customize the error response format
- Avoid global exception handling
- Return only specific validation details
This is where BindingResult becomes useful.
Using BindingResult
Simply place BindingResult immediately after the validated object.
@PostMapping("/users")
public ResponseEntity<?> createUser(
@Valid @RequestBody UserDto dto,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest()
.body(bindingResult.getAllErrors());
}
return ResponseEntity.ok("User Created Successfully");
}
Instead of throwing an exception, validation errors are now available inside the BindingResult.
Example DTO
public class UserDto {
@NotBlank(message = "Name cannot be empty")
private String name;
@Email(message = "Invalid email address")
private String email;
@Min(value = 18, message = "Age must be at least 18")
private int age;
// Getters and Setters
}
Sample Invalid Request
{
"name": "",
"email": "abc",
"age": 15
}
Validation Response
[
{
"defaultMessage": "Name cannot be empty"
},
{
"defaultMessage": "Invalid email address"
},
{
"defaultMessage": "Age must be at least 18"
}
]
No exception is thrown. Your controller handles everything.
Returning Cleaner Error Messages
Instead of returning the entire error object, you can extract only the messages.
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.toList();
return ResponseEntity.badRequest().body(errors);
}
Output:
[
"name: Name cannot be empty",
"email: Invalid email address",
"age: Age must be at least 18"
]
This response is much cleaner and easier for frontend applications to consume.
Useful BindingResult Methods
| Method | Description |
|---|---|
hasErrors() | Checks whether validation failed |
getAllErrors() | Returns all validation errors |
getFieldErrors() | Returns field-specific errors |
getFieldError() | Returns the first field error |
getErrorCount() | Returns the total number of validation errors |
Important Rule
BindingResult must be placed immediately after the validated parameter.
✅ Correct:
public ResponseEntity<?> create(
@Valid @RequestBody UserDto dto,
BindingResult bindingResult)
❌ Incorrect:
public ResponseEntity<?> create(
@Valid @RequestBody UserDto dto,
HttpServletRequest request,
BindingResult bindingResult)
If any parameter comes between @Valid and BindingResult, Spring will ignore the BindingResult and throw an exception instead.
When Should You Use BindingResult?
Use BindingResult when you want to:
- Handle validation inside the controller
- Return custom validation responses
- Avoid using global exception handlers
- Keep validation logic simple for smaller applications
For larger enterprise applications, many teams prefer using @ControllerAdvice with a global exception handler to centralize error handling. However, BindingResult is an excellent choice when controller-specific validation behavior is needed.
Conclusion
BindingResult provides a simple and effective way to handle validation errors without relying on exceptions. It gives you full control over the response format, making your APIs more user-friendly and easier for clients to consume.
If you need custom validation responses or want to avoid global exception handling for specific endpoints, BindingResult is a powerful feature worth adding to your Spring Boot toolkit.
Happy Coding! 🚀