1. 引言

在这个教程中,我们将学习如何使用三种不同的方法检查Thymeleaf中是否存在一个变量。我们将使用Spring MVC和Thymeleaf构建一个简单的Web应用,该应用有一个视图,如果给定的变量已设置,它将显示服务器日期和时间。

2. 准备工作

在深入方法之前,我们需要做一些初始设置。首先,添加Thymeleaf的依赖项:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

接下来,创建checkVariableIsDefined视图:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      th:with="lang=${#locale.language}" th:lang="${lang}">
<head>
    <title>How to Check if a Variable is Defined in Thymeleaf</title>
</head>
<body>

<!-- we'll add here the relevant code for each method -->

</body>
</html>

再定义两个新的端点来处理这个视图:

@RequestMapping(value = "/variable-defined", method = RequestMethod.GET)
public String getDefinedVariables(Model model) {
    DateFormat dateFormat = 
      DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.getDefault());
    model.addAttribute("serverTime", dateFormat.format(new Date()));
    return "checkVariableIsDefined.html";
}

@RequestMapping(value = "/variable-not-defined", method = RequestMethod.GET)
public String getNotDefinedVariables(Model model) {
    return "checkVariableIsDefined.html";
}

第一个端点加载带有serverTime变量的checkVariableIsDefined视图,而第二个端点加载不带变量的相同视图。

这个设置将帮助我们在接下来的章节中测试所介绍的方法。

3. 使用#ctx对象

我们首先探索的方法是使用上下文对象,它包含了Thymeleaf模板引擎处理模板所需的所有变量,包括用于外部化消息的Locale引用。上下文是一个独立应用的IContext接口的实现,或者对于Web应用是IWebContext接口的实现。

在Thymeleaf模板中,我们可以使用#ctx语法访问上下文对象。现在,让我们在checkVariableIsDefined视图中添加相关代码:

<div th:if="${#ctx.containsVariable('serverTime')}" th:text="'Server Time Using the #ctx Object Is: ' + ${serverTime}"/>

接下来,编写两个集成测试以验证此方法:

private static final String CTX_OBJECT_MSG = "Server Time Using the #ctx Object Is: ";

@Test
public void whenVariableIsDefined_thenCtxObjectContainsVariable() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variables-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(containsString(CTX_OBJECT_MSG)));
}

@Test
public void whenVariableNotDefined_thenCtxObjectDoesNotContainVariable() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variables-not-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(not(containsString(CTX_OBJECT_MSG))));
}

4. 使用if条件语句

接下来的方法使用if条件语句。更新checkVariableIsDefined视图:

<div th:if="${serverTime}" th:text="'Server Time Using #th:if Conditional Is: ' + ${serverTime}"/>

如果变量为null,if条件将被评估为false

现在,看看集成测试:

private static final String IF_CONDITIONAL_MSG = "Server Time Using #th:if Conditional Is: ";

@Test
public void whenVariableIsDefined_thenIfConditionalIsTrue() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variable-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(containsString(IF_CONDITIONAL_MSG)));
}

@Test
public void whenVariableIsNotDefined_thenIfConditionalIsFalse() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variable-not-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(not(containsString(IF_CONDITIONAL_MSG))));
}

if条件语句只有在以下任一条件为真时才会被评估为true

  • 变量是一个布尔值,其值为true
  • 变量是一个非零数字
  • 变量是一个非零字符
  • 变量是一个不同于"false""off""no"的字符串
  • 变量既不是布尔值、数字、字符也不是字符串

请注意,如果变量已设置,但值为"false""no""off"0,那么if条件将被评估为false,这可能会导致意外的副作用,如果我们的目的是仅检查变量是否已设置。让我们通过更新视图来说明这一点:

<div th:if='${"false"}' th:text='"Evaluating \"false\"'/>
<div th:if='${"no"}' th:text='"Evaluating \"no\"'/>
<div th:if='${"off"}' th:text='"Evaluating \"off\"'/>
<div th:if="${0}" th:text='"Evaluating 0"'/>

然后,编写集成测试:

@Test
public void whenVariableIsDefinedAndNotTrue_thenIfConditionalIsFalse() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variable-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(not(containsString("Evaluating \"false\""))))
      .andExpect(content().string(not(containsString("Evaluating \"no\""))))
      .andExpect(content().string(not(containsString("Evaluating \"off\""))))
      .andExpect(content().string(not(containsString("Evaluating 0"))));
}

我们可以解决这个问题,通过检查变量是否不为null:

<div th:if="${serverTime != null}" th:text="'Server Time Using #th:if Conditional Is: ' + ${serverTime}"/>

5. 使用unless条件语句

最后一个方法使用unless,它是if条件语句的逆否命题。相应地更新视图:

<div th:unless="${serverTime == null}" th:text="'Server Time Using #th:unless Conditional Is: ' + ${serverTime}"/>

我们也测试一下这个方法是否产生预期结果:

private static final String UNLESS_CONDITIONAL_MSG = "Server Time Using #th:unless Conditional Is: ";

@Test
public void whenVariableIsDefined_thenUnlessConditionalIsTrue() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variable-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(containsString(IF_CONDITIONAL_MSG)));
}

@Test
public void whenVariableIsNotDefined_thenUnlessConditionalIsFalse() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.get("/variable-not-defined"))
      .andExpect(status().isOk())
      .andExpect(view().name("checkVariableIsDefined.html"))
      .andExpect(content().string(not(containsString(UNLESS_CONDITIONAL_MSG))));
}

6. 总结

在这篇文章中,我们学习了在Thymeleaf中检查变量是否已定义的三种方法。第一种方法使用#ctx对象和containsVariable方法,而第二种和第三种方法使用条件语句if及其逆否命题unless

如往常一样,完整的代码可以在GitHub上找到