How do I validate form data in Spring?

In Spring MVC, the standard way to validate form data is to use Jakarta Bean Validation annotations on a form/DTO object, then check validation results in your controller with BindingResult.

Since your project uses Jakarta EE, use jakarta.validation.* imports.

1. Add validation annotations to your form object

Example form/DTO:

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class UserForm {

    @NotBlank(message = "Name is required")
    @Size(max = 100, message = "Name must be at most 100 characters")
    private String name;

    @NotBlank(message = "Email is required")
    @Email(message = "Please enter a valid email address")
    private String email;

    @NotBlank(message = "Password is required")
    @Size(min = 8, message = "Password must be at least 8 characters")
    private String password;

    // getters and setters
}

With Lombok:

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserForm {

    @NotBlank(message = "Name is required")
    @Size(max = 100, message = "Name must be at most 100 characters")
    private String name;

    @NotBlank(message = "Email is required")
    @Email(message = "Please enter a valid email address")
    private String email;

    @NotBlank(message = "Password is required")
    @Size(min = 8, message = "Password must be at least 8 characters")
    private String password;
}

2. Use @Valid in your controller

import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class UserController {

    @GetMapping("/register")
    public String showRegisterForm(Model model) {
        model.addAttribute("userForm", new UserForm());
        return "register";
    }

    @PostMapping("/register")
    public String register(
            @Valid UserForm userForm,
            BindingResult bindingResult
    ) {
        if (bindingResult.hasErrors()) {
            return "register";
        }

        // Save user or call service layer here
        return "redirect:/register/success";
    }
}

Important: BindingResult must come immediately after the validated object.

Correct:

public String register(@Valid UserForm userForm, BindingResult bindingResult)

Incorrect:

public String register(@Valid UserForm userForm, Model model, BindingResult bindingResult)

3. Display errors in Thymeleaf

If you use Thymeleaf:

<form th:action="@{/register}" th:object="${userForm}" method="post">
    <div>
        <label>Name</label>
        <input type="text" th:field="*{name}">
        <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
    </div>

    <div>
        <label>Email</label>
        <input type="email" th:field="*{email}">
        <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
    </div>

    <div>
        <label>Password</label>
        <input type="password" th:field="*{password}">
        <span th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
    </div>

    <button type="submit">Register</button>
</form>

4. Common validation annotations

@NotNull
@NotBlank
@NotEmpty
@Size(min = 2, max = 100)
@Min(18)
@Max(120)
@Email
@Pattern(regexp = "...")
@Past
@Future
@Positive
@PositiveOrZero

Use:

  • @NotNull for any value that must not be null
  • @NotBlank for strings that must contain non-whitespace text
  • @NotEmpty for strings, collections, arrays, or maps that must not be empty
  • @Size for string length or collection size
  • @Email for email format validation

5. Maven dependency

If you use Spring Boot, add:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

For Gradle:

implementation("org.springframework.boot:spring-boot-starter-validation")

6. Service-layer validation

You can also validate method parameters in Spring services:

import jakarta.validation.Valid;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

@Service
@Validated
public class UserService {

    public void createUser(@Valid UserForm userForm) {
        // business logic
    }
}

7. REST API validation example

For JSON request bodies:

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserRestController {

    @PostMapping("/api/users")
    public String createUser(@Valid @RequestBody UserForm userForm) {
        return "User created";
    }
}

For REST APIs, invalid input usually results in a 400 Bad Request.

Summary

Use this pattern:

@PostMapping("/submit")
public String submit(@Valid MyForm form, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return "form-page";
    }

    return "redirect:/success";
}

That is the typical Spring MVC form validation flow.