首页
Preview

使用Spring MVC的Thymeleaf

Thymeleaf已经迅速成为Spring MVC的事实标准(HTML)模板引擎。它拥有丰富的功能、扩展、花里胡哨的东西。但是,为了开始使用,你只需要知道一些基础知识。

在这里,我将快速介绍它们。

添加Thymeleaf到你的项目中

注意:我假设你正在使用Spring Boot和Gradle作为构建工具。如果不是这样,你可能需要执行一些额外的步骤,但是这里的关键点仍然适用。

只需将Thymeleaf添加到你的_build.gradle_文件中即可:

compile('org.springframework.boot:spring-boot-starter-thymeleaf:1.3.1.RELEASE')

为了演示功能,我们还将添加Twitter Bootstrap webjar:

compile('org.webjars:bootstrap:3.3.6')

更新:“布局方言已经作为Spring Boot 1.x中Thymeleaf起始包的一部分包含在内,但在Spring Boot 2中已被删除。”

如果你正在使用Spring Boot 2,则需要进行额外的配置步骤。点击这里查看详情。

太棒了!现在你已经将Thymeleaf作为你的服务器端HTML模板引擎。让我们开始吧。

Thymeleaf会自动从_resources/templates_目录中获取你的模板文件:

因此,请确保将所有模板放在此处,并将它们命名为whatever.html。Thymeleaf模板也是有效的HTML文件。

在开发过程中,你需要关闭Thymeleaf缓存,否则你将经常看到过时的模板版本。

这可以在你的_application.properties_文件中轻松配置:

spring.thymeleaf.cache=false

创建默认布局模板

我们将从创建一个_default layout template_开始,它将为我们所有的页面提供一个标准结构。

这通常是一个很好的实践,因为这意味着我们不必在每个新页面中重新定义样板布局。我们只需关注该页面唯一的内容。这个_default layout template_将导入我们始终想要访问的所有标准CSS和javascript资源;如果这个集合在将来发生了变化(例如,我们想要将Google Analytics代码添加到每个页面),我们只需在一个地方添加它即可。

我们将命名我们的默认布局模板为_default.html_,并将其添加到我们的_resources/templates_目录中。

以下是它的内容:

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
    <head>
        <meta charset="UTF-8"/>
        <title>Default title for my pages</title>

        <link rel="stylesheet" href="/webjars/bootstrap/3.3.6/css/bootstrap.min.css"/>
        <link rel="stylesheet" href="/webjars/bootstrap/3.3.6/css/bootstrap-theme.min.css"/>
    </head>
    <body>        <div class="container">
            <div class="row">
                <div class="col-md-3"></div>
                <div class="col-md-6" layout:fragment="content">
                        Page content goes here
                </div>
                <div class="col-md-3"></div>
            </div>
        </div>

        <script src="/webjars/jquery/1.11.1/jquery.min.js"></script>
        <script src="/webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
        <th:block layout:fragment="scripts"></th:block>
    </body>
</html>

从我们_body_标签的内容中,你可以看到我们使用了bootstrap来为我们的网站定义了一个非常基本的结构,由3列组成,只有中间的列包含任何内容。这给了我们一个基本的居中外观,留下了我们内容的左右空间。

你应该注意到几件事情:

  • Thymeleaf命名空间(th_和_layout)已在模板顶部定义。这些很重要,因为我们将在整个过程中使用它们。
  • 我们为页面指定了一个默认标题(“Default title for my pages”)。这实际上并不是必需的,只要在所有页面中覆盖它即可,但是在这里添加一个可以在许多页面想使用默认标题时很有用。
  • 通过在前面的gradle文件中包含Twitter Bootstrap,bootstrap资源会自动在/webjars/文件夹中提供。然后我们在这里包含它们,以便它们在我们所有的页面中都可用。
  • 在页面底部,我们包含了我们的javascript文件。前两个是bootstrap和jquery(都在webjars文件夹中)。第三个是我们自己需要的一个片段。我们将在稍后使用它来向我们的页面添加自定义javascript文件。
  • 最后,请注意我们布局中的中间列也是一个名为“content”的片段。这是我们将在每个页面中提供内容的片段,以便为该页面填充唯一的中央列内容。

Hello World页面

好了,我们准备创建我们的第一个页面。

让我们称之为_hello.html_,并再次将其放在_resources/templates_目录中。它将如下所示:

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="default">
    <head>
        <title>Hello world page</title>
        <link rel="stylesheet" href="/css/hello.css"/>
    </head>
    <body>
        <div layout:fragment="content">
            <h1>Hello world</h1>
        </div>
    </body>
    <th:block layout:fragment="scripts">
        <script src="/js/hello.js"></script>
    </th:block>
</html>

你可能首先注意到的是,即使我们在default.html模板中已经添加了html、head和body标签,我们在这里也要再次添加它们。这是Thymeleaf的一个特点:它被设计为每个单独的模板实际上都是可以完美渲染的。这样,任何客户端开发人员/设计师都可以拿取任何页面,只需在浏览器中编辑/打开即可,无需运行服务器。对于团队非常有用。

从此示例中需要注意的关键事项:

  • 在页面顶部附近,我们已经指定我们的“decorator”(默认布局模板)是_default.html_模板。这将告诉Thymeleaf渲染引擎使用我们的default.html模板来装饰此页面(在服务器上运行时)。
  • 我们通过将其添加到_HEAD_标签中来覆盖页面标题。
  • 我们向我们的页面添加了一个额外的CSS文件(/css/hello.css)。注意:这将不会替换default.html中包含的CSS文件,而只是添加到其中。
  • 我们通过添加H1 Hello World元素来定义“content”片段(在我们的default.html模板中声明)。这是要在我们页面的中央列中显示的内容。
  • 在页面底部,我们添加了一个额外的js文件。我们也可以将其添加到我们的HEAD元素中,但问题是,如果我们的javascript文件需要访问jquery,那么它将失败,因为jquery直到页面末尾才定义。通过将它添加到这个片段中,我们的javascript文件(/js/hello.js)是浏览器评估的最后一个文件(在jquery和bootstrap初始化之后)。不错,我们现在有了一个标准模板,以及使用它的 Hello World 示例。但是,我们如何测试它呢?

我们还有一些步骤要做。首先,我们需要把我们引用的 hello.css 和 hello.js 文件放在某个地方,其次,我们需要一个控制器来呈现页面。

现在让我们完成这些步骤。

静态资源

静态资源只需要放在 resources/static 目录下,与 resources/templates 目录并列:

由于本文的重点是 HTML 模板,而不是 CSS/JS,这是我将要提到的唯一一次有关 css/js 文件的提及。基本上,通过将你的 css 和 js 文件添加到上面显示的目录中,Spring MVC 会自动将它们提供在 /css/*.css 和 /js/*.js 上。

Hello World 控制器

我们需要的最后一件事是一个控制器来呈现模板。

Spring MVC 使得编写控制器非常简单:

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }
}

你需要注意的只有两件事:

  • 控制器将请求映射到 /hello 的页面(所以如果在本地运行,页面将在 http://localhost:8080/hello) 上可用)
  • 控制器返回“hello”,这意味着“呈现 hello.html 模板”。Thymeleaf 知道在 resources/templates 目录中查找它。

就是这样。Hello World 已经可以运行了。启动你的 Spring MVC 服务器,并将浏览器导向 http://localhost:8080/hello

你将看到以下页面内容被呈现:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Hello world page</title>
        <meta charset="UTF-8" />
        <link rel="stylesheet" href="/webjars/bootstrap/3.3.6/css/bootstrap.min.css" />
        <link rel="stylesheet" href="/webjars/bootstrap/3.3.6/css/bootstrap-theme.min.css" />
        <link rel="stylesheet" href="/css/hello.css" />
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col-md-3"></div>
                <div class="col-md-6">
                    <h1>Hello world</h1>
                </div>
                <div class="col-md-3"></div>
            </div>
        </div>
        <script src="/webjars/jquery/1.11.1/jquery.min.js"></script>
        <script src="/webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
        <script src="/js/hello.js"></script>
    </body>
</html>

注意,default.html 和 hello.html 的内容已经合并在一起了,就像我们期望的那样。

注入模型参数

接下来,让我们注入一些来自服务器的内容。

更新你的控制器如下:

@RequestMapping("/hello")
public String hello(Model model) {
    model.addAttribute("name", "Tom");
    model.addAttribute("formatted", "<b>blue</b>");
    return "hello";
}

并将你的 hello.html 文件中的“content”片段更新为以下内容:

<div layout:fragment="content">
    <span th:text="|Hello ${name}|"/>, did you know that <span th:utext="${formatted}"/> is <span th:text="${formatted}"/> in HTML?
</div>

这里,我们为我们的模型提供了两个属性(“name”和“formatted”),并将它们作为我们内容的一部分呈现。最终结果将看起来像这样:

我们在这里应用了一些技术:

  • 第一个是 th:text,它告诉 Thymeleaf:用引号中的值替换此元素的内容。
  • 第二个是 $ 语法(例如,th:text=“${formatted}”)。这允许我们将模型属性直接放入我们的内容中。
  • 第三个是“|…|”语法(例如,th:text=“|Hello ${name}|”)。这允许我们进行字符串插值,其中模型变量被放置在正常文本内部。这与 th:text=“${formatted}”不同,后者仅使用模型属性的值(没有字符串插值)。
  • 第四个是 th:utext,它告诉 Thymeleaf:不要转义被注入的文本(即 Un-escaped Text);这就是为什么蓝色实际上在上面被加粗的原因。

这就是将模型参数注入到 Thymeleaf 模板的基本原则。接下来,我们将看一些更加强大的内容。

动态列表/行的内容

通常,你会有一个要在页面上呈现的项目列表或数组。Thymeleaf 让这个变得容易。

首先,让我们更新我们的 HelloController 以包含一个项目列表:

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Model model) {
        DeveloperResource[] devResources = {
                new DeveloperResource("Google",
                     "http://www.google.com"),
                new DeveloperResource("Stackoverflow",
                     "http://www.stackoverflow.com"),
                new DeveloperResource("W3Schools",
                    "http://www.w3schools.com")
        };
        model.addAttribute("resources", devResources);
        return "hello";
    }

    public static final class DeveloperResource {
        private final String name;
        private final String url;

        public DeveloperResource(String name, String url) {
            this.name = name;
            this.url = url;
        }

        public String getName() {
            return name;
        }

        public String getUrl() {
            return url;
        }
    }
}

现在更新我们的 hello.html 模板以呈现此列表:

<div layout:fragment="content">
    <div class="list-group">
        <a th:each="resource : ${resources}" 
           th:href="${resource.url}" class="list-group-item">
            <b th:text="${resource.name}"></b>
        </a>
    </div>
</div>

在浏览器中加载此内容,呈现的 HTML 将如下所示:

<div class="list-group">
    <a class="list-group-item" href="http://www.google.com">
        <b>Google</b>
    </a>
    <a class="list-group-item" href="http://www.stackoverflow.com">
        <b>Stackoverflow</b>
    </a>
    <a class="list-group-item" href="http://www.w3schools.com">
        <b>W3Schools</b>
    </a>
</div>

而视觉结果实际上将看起来像这样:

很棒,正是我们想要的。但是,这是如何实现的呢?

让我们再次快速查看 hello.html 模板:

<div layout:fragment="content">
    <div class="list-group">
        <a th:each="resource : ${resources}" 
           th:href="${resource.url}" class="list-group-item">
            <b th:text="${resource.name}"></b>
        </a>
    </div>
</div>

“list-group”和“list-group-item”类只是 Twitter Bootstrap 类,给我们提供了上面看到的外观和感觉。

真正的 Thymeleaf 魔法来自于 th:each。它循环遍历“resources”模型属性中的每个项目(适用于任何 Java Iterable),并创建一个 Thymeleaf 变量“resource”来表示每个项目。

然后我们看到了另一个重要的 Thymeleaf 属性 th:href 的使用。这用于设置给定锚元素(<a href=“…”>…</a>)上的 href 属性。请注意,几乎可以为你想要设置的每个可能的 HTML 标记属性找到匹配的 Thymeleaf 属性(http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#setting-value-to-specific-attributes)。

最后要注意的是 Thymeleaf 的 OGNL(对象图表示语言)。它基本上是一种点表示法,允许访问 Java 对象内的属性(通过 getter)。在这里,你可以看到我们已经使用 ${resource.url} 来访问我们的 DeveloperResource 对象的 getUrl() 属性,${resource.name} 来访问 getName()。注意:只要字段都可以通过 getter 访问,它也可以在多层嵌套中工作(例如 ${resource.name.class.simpleName})。## 条件语句

如果我们只想在满足某些条件时才呈现特定的 HTML 元素或块,该怎么办呢?

我们来扩展一下我们现有的 hello.html 模板,以包含这种情况。

在我们的内容块开头添加以下行:

<span th:if="${resources.size() == 0}">No resources found.

这样,如果我们的“resources”模型属性为空,就会出现有用的信息提示消息。

只有当资源数组大小为 0 时,“No resources found” span 元素才会呈现在 HTML 中。

表单

我们通常希望使用表单将数据提交到服务器。为了在不同的场景中重用模板,能够从服务器指定表单的 action 属性非常有用。

你可能已经猜到了该怎么做:

<div layout:fragment="content">
    <form th:method="POST" th:action="${action}">
        <div class="form-group">
            <label>Name:</label>
            <input class="form-control" name="name">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
    </form>
</div>

正如预期的那样,我们只需使用 th:action 属性而不是普通的 action 属性。

然而,你还会注意到,我们还使用了 th:method 属性来指定表单 method

<div layout:fragment="content">
    <form th:method="POST" th:action="${action}">
        <div class="form-group">
            <label>Name:</label>
            <input class="form-control" name="name">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
    </form>
</div>

既然我们没有为此字段使用模型属性,为什么还要使用 Thymeleaf 语法呢?

原因在于,Thymeleaf 还自动提供了非常有用的功能,可以为我们的表单自动添加 CSRF 令牌;但是,只有当我们使用 th:actionth:method 分别设置 actionmethod 属性时,才会这样做。

这很重要,因为如果你使用 Spring Security,则你的服务器将受到 CSRF 保护(应该如此),任何 POST/PUT/DELETE 请求都将自动失败;除非它们带有 CSRF 令牌。

只需使用 Thymeleaf(th:method 和 th:action)在表单上设置 methodaction 属性,你就可以自动将 CSRF 令牌注入到你的表单中。

上述表单的渲染结果如下:

<form method="POST" action="/myaction">
    <div class="form-group">
        <label>Name:</label>
        <input class="form-control" name="name" />
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
    <input type="hidden" name="_csrf" value="e26856cf-7535-42e9-9afa-49b06aa6fa71" />
</form>

正如你所见,Thymeleaf 已经自动为你在表单底部添加了 CSRF 令牌。

好极了。

总结

Thymeleaf 比这个更多的内容(在这里详细介绍),但是这里应该足以让你入门了。

总结:

  • 将 Thymeleaf 添加到 Spring Boot MVC 项目很容易:
compile(‘org.springframework.boot:spring-boot-starter-thymeleaf:1.3.1.RELEASE’)
  • 记得在开发过程中关闭 Thymeleaf 缓存:
spring.thymeleaf.cache=false
  • layout:decoratorlayout:fragment 可用于在所有模板中重用代码。
  • th:textth:utext 将 HTML 标签的内容用服务器端内容“替换”。
  • ${name}|Hello ${name}| 分别将模型参数和字符串插值模型参数注入到模板中。
  • th:each 将循环遍历 Java 可迭代对象,并为每个项目生成内容。
  • Thymeleaf 为几乎所有标准 HTML 属性提供了自定义属性(例如 th:href)(更多详细信息在这里)。
  • Thymeleaf 的 OGNL 允许你通过 getter 访问模型参数的嵌套属性(例如 ${resource.name.class.simpleName})。
  • th:if 仅在表达式的值为 true 时呈现给定的 HTML 元素。
  • 创建表单时,始终使用 th:methodth:action,这样你就可以直接获得 CSRF 保护。

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
阿波
The minute I see you, I want your clothes gone!

评论(0)

添加评论