By WebNest Studio Engineering Team
Spring Framework Tutorial
Validation in Spring
Accepting user input safely means checking it before it reaches business logic — verifying required fields are present, numbers fall within acceptable ranges, and formats (like email addresses) are correct. Spring integrates tightly with the Bean Validation specification (Jakarta Validation, implemented by Hibernate Validator) so that these rules can be declared directly on a model class using annotations, instead of being scattered through hand-written if-checks.
By annotating a request model's fields with constraints like @NotNull and @Size, then marking the controller method's parameter with @Valid, Spring automatically validates the incoming data before your handler method body even runs, and collects any violations into a BindingResult object your method can inspect.
Common Bean Validation Constraints
These annotations live on the fields of a model/DTO class and describe the rules that field's value must satisfy.
@NotNull— the value must not be null (but an empty string or empty collection is still allowed).@NotBlank— for Strings specifically: must not be null and must contain at least one non-whitespace character.@Size(min=, max=)— restricts the length of a String or the size of a collection.@Min/@Max— restricts a numeric value to a range.@Email— validates that a String is a syntactically well-formed email address.
Triggering Validation with @Valid
Placing @Valid before a @RequestBody parameter in a controller method tells Spring to run Bean Validation on the deserialized object before the method executes. If a BindingResult parameter immediately follows the validated parameter in the method signature, Spring captures any violations into it instead of throwing an exception, letting your code decide how to respond (e.g. return a 400 with details). Without a BindingResult parameter, a failed validation instead throws a MethodArgumentNotValidException, which by default Spring MVC turns into an HTTP 400 response automatically.
Inspecting BindingResult
BindingResult exposes hasErrors() to check if any constraint was violated, and getFieldErrors() or getAllErrors() to retrieve the specific violations, each of which reports the offending field name and the associated message. This makes it possible to build a precise, field-by-field error response rather than a single generic failure message.
Example
Validating a request body with @Valid and reporting field errors via BindingResult
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Min;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
class SignupRequest {
@NotBlank(message = "name is required")
public String name;
@Email(message = "email must be valid")
public String email;
@Min(value = 18, message = "must be at least 18")
public int age;
}
@RestController
class SignupController {
@PostMapping("/signup")
public String signup(@Valid @RequestBody SignupRequest request, BindingResult result) {
if (result.hasErrors()) {
String errors = result.getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return "Validation failed: " + errors;
}
return "Signed up: " + request.name;
}
}
// POST /signup with {"name":"","email":"not-an-email","age":15}
Validation failed: name is required, email must be valid, must be at least 18
Common Mistakes
- Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation.
- Placing BindingResult somewhere other than immediately after the validated @RequestBody/@ModelAttribute parameter — Spring only recognizes it if it directly follows the validated argument.
- Confusing @NotNull with @NotBlank on a String field — @NotNull still allows an empty string ("") to pass, which is rarely what's intended for required text input.
- Not handling the case where BindingResult is omitted, then being surprised by an uncaught MethodArgumentNotValidException instead of a controlled error response.
Exercise
Using the lesson's Validating a request body with @Valid and reporting field errors via BindingResult example, create a small implementation or design sketch that demonstrates Bean Validation annotations (@NotNull, @NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on model fields.. Add a test case or scenario that would catch Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation., and describe the expected result.
Show hints
- Start with Common Bean Validation Constraints and compare your approach with Validating a request body with @Valid and reporting field errors via BindingResult. Identify the decision or behavior the lesson recommends.
- Walk through your scenario step by step. Confirm that it follows Bean Validation annotations (@NotNull, @NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on model fields. and that it would catch the failure described by Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation..
Quick quiz
1. Which choice is a mistake to avoid in Validation in Spring?
- A. Placing BindingResult somewhere other than immediately after the validated @RequestBody/@ModelAttribute parameter — Spring only recognizes it if it directly follows the validated argument.
- B. Bean Validation annotations (@NotNull, @NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on model fields.
- C. Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation.
Show answer
C. Avoid this approach: Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation. The lesson recommends: Bean Validation annotations (@NotNull, @NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on model fields.
Interview questions
A teammate is about to make this mistake: Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation. How would you diagnose and correct it using the lesson's guidance?
Show answer
Bean Validation annotations (@NotNull, @NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on model fields. Apply this in Common Bean Validation Constraints; the example Validating a request body with @Valid and reporting field errors via BindingResult shows one way to do it. Avoid Forgetting @Valid entirely — Bean Validation constraint annotations on a model class do nothing on their own unless a controller explicitly triggers validation. and explain how your implementation or design prevents that failure.
Key Points to Remember
- Bean Validation annotations (@NotNull, @NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on model fields.
- @Valid on a controller parameter triggers validation before the handler method body runs.
- A BindingResult parameter placed right after the validated argument captures violations instead of throwing an exception.
- Without BindingResult, a validation failure throws MethodArgumentNotValidException, which Spring MVC maps to an HTTP 400 by default.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering