51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

在 Spring 中使用 Thymeleaf 显示错误信息

1、概览 {#1概览}

本文将带你了解如何在 Spring 应用中使用 Thymeleaf 模板来渲染错误信息。

我们会通过一个简单的 Spring Boot 项目来进行演示,该项目是一个 "用户注册" 应用,需要验证客户端传递的各个字段,还要处理全局错误。

2、Spring Boot 应用示例 {#2spring-boot-应用示例}

创建一个简单的 Spring Boot 用户注册应用,需要一个 Controller、一个 Repository 和一个 Entity。

2.1、Maven 依赖 {#21maven-依赖}

添加所有需要的 Spring Boot Starter:Web MvcHibernate ValidationThymeleafJPA

此外,还需要一个 H2 内存数据库依赖:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-web</artifactId> 
</dependency> 
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-validation</artifactId> 
</dependency> 
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-thymeleaf</artifactId> 
</dependency> 
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency> 
<dependency> 
    <groupId>com.h2database</groupId> 
    <artifactId>h2</artifactId> 
    <scope>runtime</scope> 
    <version>1.4.200</version> 
</dependency>

2.2、实体 {#22实体}

User 实体如下:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @NotEmpty(message = "User's name cannot be empty.")
    @Size(min = 5, max = 250)
    private String fullName;

    @NotEmpty(message = "User's email cannot be empty.")
    private String email;

    @NotNull(message = "User's age cannot be null.")
    @Min(value = 18)
    private Integer age;

    private String country;

    private String phoneNumber;

    // get、set 方法省略
}

如你所见,为用户输入添加了许多验证约束。例如,字段不得为 null 或空,并限定了字符串的长短。

注意,没有对 countryphoneNumber 字段添加任何约束。这是因为我们将以它们作为示例来生成全局错误,或者与特定字段无关的错误。

2.3、Repository {#23repository}

本例中,使用一个简单的 JPA Repository:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

2.4、Controller {#24controller}

创建一个 UserController,注入 UserRepository

@Controller
public class UserController {

    @Autowired
    private UserRepository repository;
    @GetMapping("/add")
    public String showAddUserForm(User user) {
        return "errors/addUser";
    }

    @PostMapping("/add")
    public String addUser(@Valid User user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "errors/addUser";
        }
        repository.save(user);
        model.addAttribute("users", repository.findAll());
        return "errors/home";
    }
}

如上,在 /add 路径下定义了一个 @GetMapping,用于显示注册表单。位于相同路径的 @PostMapping 用于在提交表单时进行验证,如果一切顺利,则随后保存到 Repository。

3、使用 Thymeleaf 模板显示错误消息 {#3使用-thymeleaf-模板显示错误消息}

基础知识介绍完毕后,进入关键的环节,即创建 UI 模板和显示错误信息(如果有的话)。

3.1、显示字段错误 {#31显示字段错误}

Thymeleaf 提供了一个内置的 field.hasErrors 方法,可根据给定字段是否存在错误返回一个布尔值。将该方法与 th:if 结合使用,可以选择是否显示存在的错误:

<p th:if="${#fields.hasErrors('age')}">Invalid Age</p>

如果要添加任何样式,可以有条件地使用 th:class

<p  th:if="${#fields.hasErrors('age')}" th:class="${#fields.hasErrors('age')}? error">
  Invalid Age</p>

CSS class error 会将元素变为红色:

<style>
    .error {
        color: red;
    }
</style>

Thymeleaf 的另一个属性 th:errors 能够显示指定 selector(例如 email)上的所有错误:

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

在上面的代码段中,还可以看到 CSS 样式的另一种用法。使用 th:errorclass,这样就不需要使用任何条件属性来应用 CSS 了。

或者,可以选择使用 th:each 遍历给定字段上的所有验证信息(validation message):

<div>
    <label for="fullName">Name</label> <input type="text" th:field="*{fullName}" 
      id="fullName" placeholder="Full Name">
    <ul>
        <li th:each="err : ${#fields.errors('fullName')}" th:text="${err}" class="error" />
    </ul>
</div>

注意,在这里使用了另一个 Thymeleaf 方法 fields.errors(),以收集后端应用针对 fullName 字段返回的所有验证信息。

现在,进行测试。启动 Boot 应用并访问 http://localhost:8080/add 端点。

如下就是未提供任何输入时的 UI 界面。

Thymeleaf fieldErrs

3.2、一次性显示所有错误 {#32一次性显示所有错误}

接下来,看看如何将所有错误信息集中显示,而不是逐一显示。

为此,可以使用 Thymeleaf 的 fields.hasAnyErrors() 方法:

<div th:if="${#fields.hasAnyErrors()}">
    <ul>
        <li th:each="err : ${#fields.allErrors()}" th:text="${err}" />
    </ul>
</div>

如你所见,在这里使用了另一个变量 fields.allErrors(),以遍历 HTML 表单上所有字段的所有错误。

可以使用 #fields.hasErrors('*') 代替 fields.hasAnyErrors()。同样,#fields.errors('*') 也可以替代上面使用的 #fields.allErrors()

效果如下:

Thymeleaf allErrors

3.3、在表单外显示错误 {#33在表单外显示错误}

考虑一下在 HTML 表单外显示验证信息的情况。

在这种情况下,不要使用 selection 或 (*{...}),只需使用格式为 (${...}) 的全限定变量名即可:

<h4>Errors on a single field:</h4>
<div th:if="${#fields.hasErrors('${user.email}')}"
 th:errors="*{user.email}"></div>
<ul>
    <li th:each="err : ${#fields.errors('user.*')}" th:text="${err}" />
</ul>

这会在 email 字段中显示所有错误信息。

现在,来看看如何一次性显示所有信息:

<h4>All errors:</h4>
<ul>
<li th:each="err : ${#fields.errors('user.*')}" th:text="${err}" />
</ul>

页面上看到的内容如下:

Thymeleaf outsideForm

3.4、显示全局错误 {#34显示全局错误}

在实际情况中,可能会存在与特定字段无关的错误。我们可能会遇到一种情况,需要考虑多个输入以验证业务条件。这些错误被称为全局错误。

举一个简单的例子。对于 countryphoneNumber 字段,可以添加一个检查,即对于给定的国家,电话号码应以特定的前缀开头。

首先,添加一个 Service 来执行验证:

@Service
public class UserValidationService {
    public String validateUser(User user) {
        String message = "";
        if (user.getCountry() != null && user.getPhoneNumber() != null) {
            if (user.getCountry().equalsIgnoreCase("India") 
              && !user.getPhoneNumber().startsWith("91")) {
                message = "Phone number is invalid for " + user.getCountry();
            }
        }
        return message;
    }
}

如上,添加了一个案例。对于印度这个国家,电话号码应以前缀 91 开头。

然后,调整 Controller 的 @PostMapping

@PostMapping("/add")
public String addUser(@Valid User user, BindingResult result, Model model) {
    String err = validationService.validateUser(user);
    if (!err.isEmpty()) {
        ObjectError error = new ObjectError("globalError", err);
        result.addError(error);
    }
    if (result.hasErrors()) {
        return "errors/addUser";
    }
    repository.save(user);
    model.addAttribute("users", repository.findAll());
    return "errors/home";
}

最后,在 Thymeleaf 模板中,添加常量 global 来显示此类错误:

<div th:if="${#fields.hasErrors('global')}">
    <h3>Global errors:</h3>
    <p th:each="err : ${#fields.errors('global')}" th:text="${err}" class="error" />
</div>

另外,也可以使用 #fields.hasGlobalErrors()#fields.globalErrors() 方法来代替常量,达到同样的目的。

在输入无效输入时看到的结果如下:

Thymeleaf global|390x375

4、总结 {#4总结}

本文介绍了如何在 Spring Boot 中使用 Thymeleaf 显示错误信息,包括显示单个字段错误、一次性显示所有字段错误、在 HTML 表单外显示错误以及显示全局错误。


Ref:https://www.baeldung.com/spring-thymeleaf-error-messages

赞(3)
未经允许不得转载:工具盒子 » 在 Spring 中使用 Thymeleaf 显示错误信息