Thymeleaf中BindingResult未校验输入问题求助
I've been stuck on this for hours: My Spring Boot controller has @GetMapping and @PostMapping methods, and I use @ControllerAdvice to handle database validation errors, but that's not the issue here. I expect BindingResult to flag validation errors for the firstName and lastName fields, but the error messages aren't being passed to the Thymeleaf template. Relevant code is below:
@GetMapping Method
@GetMapping("/register") public String showRegistrationForm(Model model, @RequestParam(required = false) boolean registrationError) { UserRegistrationForm registrationForm = new UserRegistrationForm(); model.addAttribute("registrationForm", registrationForm); model.addAttribute("registerError", registrationError); return "registration"; }
@PostMapping Method
@PostMapping("/register") public String handleRegistration(@ModelAttribute @Valid UserRegistrationForm userRegistrationForm, BindingResult result, Model model) { if(result.hasErrors()){ model.addAttribute("registrationForm", userRegistrationForm); return "registration"; } UserDTO userDTO = new UserDTO(); userDTO.setEmail(userRegistrationForm.getEmail()); userDTO.setPassword(passwordEncoder.encode(userRegistrationForm.getPassword())); userDTO.setFirstName(userRegistrationForm.getFirstName()); userDTO.setLastName(userRegistrationForm.getLastName()); userDTO.setRoleName(Set.of("ROLE_USER")); userDTO.setUsername(userRegistrationForm.getUsername()); User users = new User(); users.setUsername(userDTO.getUsername()); users.setEmail(userDTO.getEmail()); users.setPassword(userDTO.getPassword()); users.setFirstName(userDTO.getFirstName()); users.setLastName(userDTO.getLastName()); users.setRoleName(userDTO.getRoleName()); try { userService.saveUser(users); System.out.println(repository.findByEmail(users.getEmail())); } catch (Exception e) { if (repository.findByUsername(users.getUsername()).isPresent() && repository.findByEmail(users.getEmail()).isPresent()){ logger.warn(String.format("User already exists with the username: %s and the email: %s", users.getUsername(), users.getEmail())); throw new UserException(String.format("User already exists with the username: %s and the email: %s", users.getUsername(), users.getEmail())); } else if (repository.findByUsername(users.getUsername()).isPresent()) { logger.warn(String.format("User already exists with the username: %s", users.getUsername())); throw new UserException(String.format("User already exists with the username: %s", users.getUsername())); } else if (repository.findByEmail(users.getEmail()).isPresent()) { logger.warn(String.format("User already exists with the email: %s", users.getEmail())); throw new UserException(String.format("User already exists with the email: %s", users.getEmail())); } } return "redirect:/login?registrationSuccess=true"; }
Thymeleaf Template Snippet
<div class="d-flex flex-row align-items-center mb-4"> <i class="fas fa-user fa-lg me-3 fa-fw"></i> <div class="form-outline flex-fill mb-0"> <label class="form-label" for="firstName">Name</label> <input type="text" id="firstName" th:field="*{firstName}" class="form-control" placeholder="First name"/> </div> <div th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}" class="alert alert-warning"></div> </div> <div class="d-flex flex-row align-items-center mb-4"> <i class="fas fa-user fa-lg me-3 fa-fw"></i> <div class="form-outline flex-fill mb-0"> <label class="form-label" for="lastName">Last name</label> <input type="text" id="lastName" th:field="*{lastName}" class="form-control" placeholder="Last name"/> </div> <div th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}" class="alert alert-warning"></div> </div>
UserRegistrationForm Class with Validation Constraints
@AllArgsConstructor @NoArgsConstructor @Data @Entity @Table public class UserRegistrationForm { @Id @SequenceGenerator(name = "user_Reg_Form_Gen", allocationSize = 1, sequenceName = "user_Reg_Form_Gen") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_Reg_Form_Gen") Long registerFormID; @NotNull(message = "Username is required") @Size(min = 3, max = 16, message = "Username must be between 3 and 16 characters") private String username; @NotNull(message = "Please enter a password") private String password; private String matchPassword; @NotNull(message = "First name is required") private String firstName; @NotNull(message = "Last name is required") private String lastName; @NotNull(message = "Email is required") private String email; }
Solutions
1. Remove JPA Annotations from UserRegistrationForm
The root issue is that you've marked UserRegistrationForm as a JPA entity with @Entity and @Table, plus ID generation annotations. This turns it into a database entity instead of a simple form DTO, which breaks Spring's validation and model binding flow. These annotations belong on your User entity, not the form object.
Updated UserRegistrationForm:
@AllArgsConstructor @NoArgsConstructor @Data public class UserRegistrationForm { @NotNull(message = "Username is required") @Size(min = 3, max = 16, message = "Username must be between 3 and 16 characters") private String username; @NotNull(message = "Please enter a password") private String password; private String matchPassword; @NotNull(message = "First name is required") private String firstName; @NotNull(message = "Last name is required") private String lastName; @NotNull(message = "Email is required") private String email; }
2. Simplify Model Handling in @PostMapping
When validation fails, you don't need to manually re-add userRegistrationForm to the model. Spring automatically retains the validated object with its BindingResult errors when you return the view name. Clean up your method:
@PostMapping("/register") public String handleRegistration(@ModelAttribute @Valid UserRegistrationForm userRegistrationForm, BindingResult result) { if (result.hasErrors()) { return "registration"; } // Rest of your registration logic }
3. Confirm Thymeleaf Form Binding
Ensure your template's <form> tag uses th:object="${registrationForm}" to bind to the form object. This is mandatory for *{firstName} and #fields.hasErrors('firstName') to work correctly:
<form th:action="@{/register}" th:object="${registrationForm}" method="post"> <!-- Your existing form fields go here --> </form>
4. Validate Dependencies
Make sure your project includes the Spring Boot Validation starter—this provides the underlying engine for @Valid to function:
Maven (pom.xml):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
Gradle (build.gradle):
implementation 'org.springframework.boot:spring-boot-starter-validation'
Content sourced from Stack Exchange, question author Jschwery

