Spring Boot+Thymeleaf项目表单验证失效问题求助
Spring Boot + Thymeleaf表单验证失效问题排查
问题现象
提交空表单时,@NotEmpty、@Pattern等验证注解未生效,直接触发NullPointerException,日志显示绑定结果无错误。
依赖配置(pom.xml)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.5.Final</version> </dependency>
DTO类代码
package com.example.randomtext.web; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.ToString; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; @Getter @Setter @ToString public class MainPageForm { @NotEmpty(message = "choose the source file") private SourceFile sourceFile; private String sourceText; private boolean sourceTextSet; private String generatedText; @Pattern(regexp = "[0-9]+", message = "input the whole number") private String lengthOfOutput; @RequiredArgsConstructor @Getter public enum SourceFile { ALICE("alice.txt"), CONFUCIUS("confucius.txt"), private static final SourceFile[] ALL_VALUES = SourceFile.values(); private final String fileName; } }
Thymeleaf表单(main.html)
<html> <head><title>RandomText</title></head> <body> <h3>Random text generator</h3> <pre th:if="${!mainPageForm.sourceTextSet}" th:style="'color:red;'"> source text not set. Have you chosen the source file?</pre> <form action="#" th:action="@{/random_text}" th:object="${mainPageForm}" method="post"> <label>source file</label><br> <div th:each="sourceFile : ${T(com.example.randomtext.web.MainPageForm.SourceFile).values()}"> <input type="radio" th:field="*{sourceFile}" th:value="${sourceFile}"> <label th:text="${sourceFile.getFileName()}">source file</label> <pre th:style="'color:red;'" th:if="${#fields.hasErrors('sourceFile')}" th:errors="*{sourceFile}"></pre> <br> </div> <label>length of string to be generated<br> <input type="text" width="200" th:field="*{lengthOfOutput}"> <pre th:style="'color:red;'" th:if="${#fields.hasErrors('lengthOfOutput')}" th:errors="*{lengthOfOutput}"></pre> </label><br><br> <br><input type="submit" value="Submit"/><input type="reset" value="Reset"/> </form> <pre th:text="${mainPageForm.sourceText}" th:style="'color:black;'"> source text</pre> </body> </html>
控制器代码
package com.example.randomtext.web; import com.example.randomtext.RandomTextService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import javax.validation.Valid; import java.net.URISyntaxException; import static com.example.randomtext.Constants.BASE_PATH; import static com.example.randomtext.Constants.MAIN_TEMPLATE; @Controller @RequiredArgsConstructor @Slf4j public class MainPageController { private final RandomTextService service; @GetMapping(BASE_PATH) public String doGet(MainPageForm form) { return MAIN_TEMPLATE; } @PostMapping(BASE_PATH) public String doPost(@Valid MainPageForm mainPageForm, BindingResult bindingResult) throws URISyntaxException { log.info("got form data {}", mainPageForm.toString()); if (!bindingResult.hasErrors()) { log.info("see no errors :E"); log.info(bindingResult.toString()); log.info("sourceFile {}", mainPageForm.getSourceFile().getFileName()); mainPageForm.setSourceText(service.readSourceString(mainPageForm.getSourceFile().getFileName())); } else { log.info("got errors"); bindingResult.getFieldErrors() .forEach(e -> log.error("got validation error {} for field {}", e.getDefaultMessage(), e.getField())); } return MAIN_TEMPLATE; } }
提交空表单后的日志
2023-01-30T20:22:49.481+03:00 INFO 5464 --- [p-nio-80-exec-6] c.e.randomtext.web.MainPageController : got form data MainPageForm(sourceFile=null, sourceText=null, sourceTextSet=false, generatedText=null, lengthOfOutput=) 2023-01-30T20:22:49.481+03:00 INFO 5464 --- [p-nio-80-exec-6] c.e.randomtext.web.MainPageController : see no errors :E 2023-01-30T20:22:49.481+03:00 INFO 5464 --- [p-nio-80-exec-6] c.e.randomtext.web.MainPageController : org.springframework.validation.BeanPropertyBindingResult: 0 errors 2023-01-30T20:22:49.483+03:00 ERROR 5464 --- [p-nio-80-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Cannot invoke "com.example.randomtext.web.MainPageForm$SourceFile.getFileName()" because the return value of "com.example.randomtext.web.MainPageForm.getSourceFile()" is null] with root cause
已尝试操作
- 调整
javax.validation和hibernate-validator依赖版本 - 执行maven clean命令
解决方案
1. 修正枚举类型的验证注解
@NotEmpty仅适用于字符串、集合、数组等类型,枚举类型的非空验证需使用@NotNull:
@NotNull(message = "choose the source file") private SourceFile sourceFile;
2. 移除冗余依赖
spring-boot-starter-validation已包含validation-api和hibernate-validator的适配版本,手动引入易导致版本冲突,删除pom.xml中以下内容:
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.5.Final</version> </dependency>
3. 完善长度字段验证
@Pattern仅验证格式,空字符串不会触发该规则,需添加@NotEmpty确保输入不为空:
@NotEmpty(message = "length cannot be empty") @Pattern(regexp = "[0-9]+", message = "input the whole number") private String lengthOfOutput;
4. 确保枚举值绑定正确
Thymeleaf中枚举值绑定需传递枚举名称,修改radio的value属性:
<input type="radio" th:field="*{sourceFile}" th:value="${sourceFile.name()}">
完成以上调整后,重启项目即可触发正常的表单验证,避免空指针异常。
内容的提问来源于stack exchange,提问作者Ignat
相关产品推荐
相关产品推荐

