Thymeleaf 是一款用於渲染 XML/XHTML/HTML5 內容的模板引擎。類似 JSP,Velocity,FreeMaker 等,它也可以輕易的與 Spring MVC 等 Web 框架進行集成作為 Web 應用的模板引擎。與其它模板引擎相比,Thymeleaf 最大的特點是能夠直接在瀏覽器中打開並正確顯示模板頁面,而不需要啟動整個 Web 應用。
Thymeleaf 初探
相比於其他的模板引擎,Thymeleaf 最大的特點是通過 HTML 的標簽屬性渲染標簽內容,以下是一個 Thymeleaf 模板例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>
<body>
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
</body>
</html>
|
這是一段標准的 HTML 代碼,這也就意味着通過瀏覽器直接打開它是可以正確解析它的結構並看到頁面的樣子。相比去其他的模板引擎在指定的位置通過${}
等表達式進行渲染,Thymeleaf 則是一種針對 HTML/XML 定制的模板語言(當然它可以被擴展),它通過標簽中的th:text
屬性來填充該標簽的一段內容。上例中,<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
意味着<p>
標簽中的內容會被表達式#{home.welcome}
的值所替代,無論模板中它的內容是什么,之所以在模板中 “多此一舉 “地填充它的內容,完全是為了它能夠作為原型在瀏覽器中直接顯示出來。
標准表達式語法
變量
Thymeleaf 模板引擎在進行模板渲染時,還會附帶一個 Context 存放進行模板渲染的變量,在模板中定義的表達式本質上就是從 Context 中獲取對應的變量的值:
1
|
<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>
|
假設today
的值為2015年8月14日
,那么渲染結果為:<p>Today is: 2015年8月14日.</p>
。可見 Thymeleaf 的基本變量和 JSP 一樣,都使用${.}
表示獲取變量的值。
URL
URL 在 Web 應用模板中占據着十分重要的地位,需要特別注意的是 Thymeleaf 對於 URL 的處理是通過語法@{...}
來處理的。Thymeleaf 支持絕對路徑 URL:
1
|
<a th:href="@{http://www.thymeleaf.org}">Thymeleaf</a>
|
同時也能夠支持相對路徑 URL:
- 當前頁面相對路徑 URL——
user/login.html
,通常不推薦這樣寫。 - Context 相關 URL——
/static/css/style.css
另外,如果需要 Thymeleaf 對 URL 進行渲染,那么務必使用th:href
,th:src
等屬性,下面是一個例子
1
2
3
4
5
6
7
8
9
|
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
|
幾點說明:
- 上例中 URL 最后的 (orderId=${o.id}) 表示將括號內的內容作為 URL 參數處理,該語法避免使用字符串拼接,大大提高了可讀性
@{...}
表達式中可以通過{orderId}
訪問 Context 中的orderId
變量@{/order}
是 Context 相關的相對路徑,在渲染時會自動添加上當前 Web 應用的 Context 名字,假設 context 名字為 app,那么結果應該是/app/order
如果 URLs 寫在 style 的屬性里,那么
1
|
<div class="fill" style="background-image:url('../../style/images/bg_shadow_left.png');"></div>
|
替換寫成 style 前面加 th: 鏈接的前后加上 + 號運算符,如下:
1
|
<div class="fill" th:style="'background-image:url(' + @̣{/images/bg_shadow_left.png} + ');'"></div>
|
字符串替換
很多時候可能我們只需要對一大段文字中的某一處地方進行替換,可以通過字符串拼接操作完成:
1
|
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
|
一種更簡潔的方式是:
1
|
<span th:text="|Welcome to our application, ${user.name}!|">
|
當然這種形式限制比較多,|...|
中只能包含變量表達式${...}
,不能包含其他常量、條件表達式等。
運算符
在表達式中可以使用各類算術運算符,例如 +, -, *, /, %
1
|
th
:with="isEven=(${prodStat.count} % 2 == 0)"
|
邏輯運算符>
, <
, <=
,>=
,==
,!=
都可以使用,唯一需要注意的是使用<
,>
時需要用它的 HTML 轉義符:
1
2
|
th
:if="${prodStat.count} > 1"
th
:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')"
|
循環
渲染列表數據是一種非常常見的場景,例如現在有 n 條記錄需要渲染成一個表格<table>
,該數據集合必須是可以遍歷的,使用th:each
標簽:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<body>
<h1>Product list</h1>
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>
<p>
<a href="../home.html" th:href="@{/}">Return to home</a>
</p>
</body>
|
可以看到,需要在被循環渲染的元素(這里是<tr>
)中加入th:each
標簽,其中th:each="prod : ${prods}"
意味着對集合變量prods
進行遍歷,循環變量是prod
在循環體中可以通過表達式訪問。
條件求值
If/Unless
Thymeleaf 中使用th:if
和th:unless
屬性進行條件判斷,下面的例子中,<a>
標簽只有在th:if
中條件成立時才顯示:
1
|
<a th:href="@{/login}" th:unless=${session.user != null}>Login</a>
|
th:unless
於th:if
恰好相反,只有表達式中的條件不成立,才會顯示其內容。
<a th:href="@{/login}" th:if=${session.user==null}>Login</a>
Switch
Thymeleaf 同樣支持多路選擇 Switch 結構:
1
2
3
4
|
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
</div>
|
默認屬性 default 可以用*
表示:
1
2
3
4
5
|
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
|
Utilities
為了模板更加易用,Thymeleaf 還提供了一系列 Utility 對象(內置於 Context 中),可以通過#
直接訪問:
#dates
#calendars
#numbers
#strings
arrays
lists
sets
maps
- …
下面用一段代碼來舉例一些常用的方法:
#dates
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/*
* Format date with the specified pattern
* Also works with arrays, lists or sets
*/
${#dates.format(date, 'dd/MMM/yyyy HH:mm')}
${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')}
${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')}
${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')}
/*
* Create a date (java.util.Date) object for the current date and time
*/
${#dates.createNow()}
/*
* Create a date (java.util.Date) object for the current date (time set to 00:00)
*/
${#dates.createToday()}
|
#strings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/*
* Check whether a String is empty (or null). Performs a trim() operation before check
* Also works with arrays, lists or sets
*/
${#strings.isEmpty(name)}
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
${#strings.setIsEmpty(nameSet)}
/*
* Check whether a String starts or ends with a fragment
* Also works with arrays, lists or sets
*/
${#strings.startsWith(name,'Don')} // also array*, list* and set*
${#strings.endsWith(name,endingFragment)} // also array*, list* and set*
/*
* Compute length
* Also works with arrays, lists or sets
*/
${#strings.length(str)}
/*
* Null-safe comparison and concatenation
*/
${#strings.equals(str)}
${#strings.equalsIgnoreCase(str)}
${#strings.concat(str)}
${#strings.concatReplaceNulls(str)}
/*
* Random
*/
${#strings.randomAlphanumeric(count)}
|
頁面即原型
在 Web 開發過程中一個繞不開的話題就是前端工程師與后端工程師的協作,在傳統 Java Web 開發過程中,前端工程師和后端工程師一樣,也需要安裝一套完整的開發環境,然后各類 Java IDE 中修改模板、靜態資源文件,啟動 / 重啟 / 重新加載應用服務器,刷新頁面查看最終效果。
但實際上前端工程師的職責更多應該關注於頁面本身而非后端,使用 JSP,Velocity 等傳統的 Java 模板引擎很難做到這一點,因為它們必須在應用服務器中渲染完成后才能在瀏覽器中看到結果,而 Thymeleaf 從根本上顛覆了這一過程,通過屬性進行模板渲染不會引入任何新的瀏覽器不能識別的標簽,例如 JSP 中的<form:input>
,不會在 Tag 內部寫表達式。整個頁面直接作為 HTML 文件用瀏覽器打開,幾乎就可以看到最終的效果,這大大解放了前端工程師的生產力,它們的最終交付物就是純的 HTML/CSS/JavaScript 文件。
Thymeleaf 之 內置對象、定義變量、URL參數及標簽自定義屬性
如標題所述,這篇文章主要講述Thymeleaf中的內置對象(list解析、日期格式化、數字格式化等)、定義變量、獲取URL的參數和在頁面標簽中自定義屬性的應用。
如果對Thymeleaf的基本使用、maven依賴等不清楚的可以先閱讀我的另一篇文章《Thymeleaf 之 初步使用》。
- Controller部份
@Controller public class IndexController { @GetMapping(value = "index") public String index(Model model, HttpServletRequest request) { List<String> datas = new ArrayList<String>(); datas.add("知識林"); datas.add("http://www.zslin.com"); datas.add("393156105"); model.addAttribute("datas", datas); model.addAttribute("curDate", new Date()); model.addAttribute("money", Math.random()*100); return "index"; } }
在這個控制器的Model中存放了這樣幾個數據:一個String類型的列表、一個日期對象和一個數值,這些東西在實際應用開發過程中應用非常廣泛,下面具體看一下在Thymeleaf中是如何解析這些數據的。
- 日期格式化
<span th:text="${#dates.format(curDate, 'yyyy-MM-dd HH:mm:ss')}"></span>
說明: 使用內置對象dates
的format
函數即可對日期進行格式化,在format
函數中,第一個參數是日期對象,對二兩個參數為日期格式(規則跟SimpleDateFormat
一樣)
需要注意的是:
· 內置對象一般都以s
結尾,如dates
、lists
、numbers
等
· 在使用內置對象是在對象名前都需要加#
號。
- 數字格式化
<span th:text="${#numbers.formatDecimal(money, 0, 2)}"></span>
說明: 此示例表示保留兩位小數位,整數位自動;
<span th:text="${#numbers.formatDecimal(money, 3, 2)}"></span>
說明: 此示例表示保留兩位小數位,3位整數位(不夠的前加0)
- 獲取列表長度
<span th:text="${#lists.size(datas)}"></span>
說明: 使用#lists.size
來獲取List的長度。
- 獲取URL參數值
<span th:text="${#httpServletRequest.getParameter('page')}"></span>
說明: 當訪問http://localhost:1105/index?page=5
時頁面將會得到page
對應的值:5
。
- 定義變量
<div th:with="curPage=${#httpServletRequest.getParameter('page')}"> <h3>當前頁碼:<span th:text="${curPage}"></span></h3> </div>
說明: 同樣,當訪問http://localhost:1105/index?page=5
時,頁面將顯示:當前頁碼:5
,說明用th:with
來定義變量,多個用,
號隔開,使用范圍在當前標簽內。
- 自定義標簽屬性
在Thymeleaf中可以使用th:
加上標簽的任何屬性進行賦值,但有些時候會遇到自定義的屬性,再用th:
加自定義的屬性則會無效。比如:需要對<span>
標簽增加objName
和objId
這樣的屬性,在非Thymeleaf情況下是這樣:
<span objId="1" objName="知識林"></span>
變量情況是:
<span objId="${obj.id}" objName="${obj.name}"></span>
在Thymeleaf下則是:
<span th:attr="myDate=${#dates.format(curDate, 'yyyy-MM-dd')}, myMoney=${money}"></span>
說明: 在頁面上查看源代碼可以看到:<span myMoney="91.6059494319957" myDate="2016-31-02"></span>
,說明自定義屬性用:th:attr
,多個屬性用,
隔開。
- 內置對象
上面簡單描述了比較常用的dates
、lists
、numbers
這幾個內置對象,在Thymeleaf還有很多的內置對象,像strings
也非常常用,用法跟java.lang.String
類的用法一樣。
在Thymeleaf中的內置對象有:
#dates
:日期格式化內置對象,具體方法可以參照java.util.Date
;
#calendars
:類似於#dates
,但是是java.util.Calendar
類的方法;
#numbers
: 數字格式化;
#strings
:字符串格式化,具體方法可以參照java.lang.String
,如startsWith
、contains
等;
#objects
:參照java.lang.Object
;
#bools
:判斷boolean類型的工具;
#arrays
:數組操作的工具;
#lists
:列表操作的工具,參照java.util.List
;
#sets
:Set操作工具,參照java.util.Set
;
#maps
:Map操作工具,參照java.util.Map
;
#aggregates
:操作數組或集合的工具;
#messages
:操作消息的工具。
thymeleaf+bootstrap,onclick傳參
<span title="刪除" th:onclick="'javascript:delCfmModel(\'/user/' + ${user.id} + '/delete\')'" style="cursor: pointer" class="glyphicon glyphicon-trash"> </span>
或 使用字符串拼接的另外一種簡潔寫法:<span th:text="|Welcome to our application, ${user.name}!|">
<span title="刪除" th:onclick="|javascript:delCfmModel('/sys/user/${user.id}/delete')|" style="cursor: pointer" class="glyphicon glyphicon-trash"> </span>
如果參數是字符串,需要加引號,如上圖所示。否則會報錯:Uncaught SyntaxError:Invalid regular expressiong flags
如果只傳數字,可以不加引號,如下圖所示:
<a href="#editModal" role="button" data-toggle="modal" th:onclick="'javascript:editUser('+${prod.id}+');'"> Edit </a> <script> function editUser(id) { $.get("/projectName/user/edit", {objectid: id}, function (data) { $("#frm_container1").html(data); }); } </script>
https://yq.aliyun.com/articles/60696
<!-- 信息刪除確認 --> <div class="modal fade" id="deleteModal"> <div class="modal-dialog"> <div class="modal-content message_align"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h4 class="modal-title">Delete</h4> </div> <div class="modal-body" style="text-align: center"> <p> <i class="glyphicon glyphicon-alert"></i> Do You Really Want to Delete This? </p> </div> <div class="modal-footer"> <input type="hidden" id="modalUrl"/> <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> <a id="modalConfirmBtn" class="btn btn-success" data-dismiss="modal">Delete</a> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal -->
<script type="text/javascript"> function delCfmModel(url) { $('#modalUrl').val(url);//給會話中的隱藏屬性URL賦值 $('#deleteModal').modal(); } $("#modalConfirmBtn").click(function () { $.get($('#modalUrl').val(),function () { window.location.reload(); //重新加載頁面 }); }); </script>
如果需要彈出的Modal窗口垂直居中,水平居中,可以添加以下css樣式(這個樣式是覆蓋了bootstrap的。tips,這個css是為上面的Modal div深度定制的,如需更改,需確定上面的html是否同步更改)
.modal { text-align: center; } @media screen and (min-width: 768px) { .modal:before { display: inline-block; vertical-align: middle; content: " "; height: 100%; } } .modal-dialog { display: inline-block; text-align: left; vertical-align: middle; }
Standard Expression syntax Most Thymeleaf attributes allow their values to be set as or containing expressions,
which we will call Standard Expressions because of the dialects they are used in. These can be of five types:
${...} : Variable expressions.
*{...} : Selection expressions.
#{...} : Message (i18n) expressions.
@{...} : Link (URL) expressions.
~{...} : Fragment expressions.
2017-09-06 23:06:20.026 WARN 9824 --- [nio-8081-exec-1] n.n.u.t.expressions.ExpressionProcessor :
Fragment expression "layout/default" is being wrapped as a Thymeleaf 3 fragment expression (~{...}) for backwards compatibility purposes. "
This wrapping will be dropped in the next major version of the expression processor,
so please rewrite as a Thymeleaf 3 fragment expression to future-proof your code.
See https://github.com/thymeleaf/thymeleaf/issues/451 for more information.
消除掉上面的WARN日志,
use:
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/default}">
replace
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="layout/default">
http://www.thymeleaf.org/doc/articles/standarddialect5minutes.html
Create a common layout to be used for several pages, defining extension points in the body with the layout:fragment
/data-layout-fragment
processors:
<!DOCTYPE html>
<html>
<head> <title>Layout page</title> <script src="common-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section layout:fragment="content"> <p>Page content goes here</p> </section> <footer> <p>My footer</p> <p layout:fragment="custom-footer">Custom footer here</p> </footer> </body> </html>
Create a content page that will use the layout, defining any HTML to use for the extension points in the layout, and specified by a layout:decorate
/data-layout-decorate
processor at the root element of your page:
<html layout:decorate="~{layout.html}"> <head> <title>Content page</title> <script src="content-script.js"></script> </head> <body> <section layout:fragment="content"> <p>This is a paragraph from the content page</p> </section> <footer> <p layout:fragment="custom-footer">This is some footer content from the content page</p> </footer> </body> </html>
Get Thymeleaf to process your content page. The result will be the layout template decorated by your content page, meaning that the content page will fill out the layout's extension points, replace titles, and merge <head>
items:
<!DOCTYPE html>
<html>
<head> <title>Content page</title> <script src="common-script.js"></script> <script src="content-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section> <p>This is a paragraph from the content page</p> </section> <footer> <p>My footer</p> <p>This is some footer content from the content page</p> </footer> </body> </html>
Intrigued? Check out the documentation links near the top of this readme to learn more.
https://github.com/ultraq/thymeleaf-layout-dialect
https://ultraq.github.io/thymeleaf-layout-dialect/
[Please make sure to select the branch corresponding to the version of Thymeleaf you are using]
Status
This is a thymeleaf extras module, not a part of the Thymeleaf core (and as such following its own versioning schema), but fully supported by the Thymeleaf team.
This repository contains two projects:
- thymeleaf-extras-springsecurity3 for integration with Spring Security 3.x
- thymeleaf-extras-springsecurity4 for integration with Spring Security 4.x
Current versions:
- Version 3.0.2.RELEASE - for Thymeleaf 3.0 (requires Thymeleaf 3.0.3+)
- Version 2.1.3.RELEASE - for Thymeleaf 2.1 (requires Thymeleaf 2.1.2+)
Features
This module provides a new dialect called org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect
or org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect
(depending on the Spring Security version), with default prefix sec
. It includes:
- New expression utility objects:
#authentication
representing the Spring Security authentication object (an object implementing theorg.springframework.security.core.Authentication
interface).#authorization
: a expression utility object with methods for checking authorization based on expressions, URLs and Access Control Lists.- New attributes:
sec:authentication="prop"
outputs aprop
property of the authentication object, similar to the Spring Security<sec:authentication/>
JSP tag.sec:authorize="expr"
orsec:authorize-expr="expr"
renders the element children (tag content) if the authenticated user is authorized to see it according to the specified Spring Security expression.sec:authorize-url="url"
renders the element children (tag content) if the authenticated user is authorized to see the specified URL.sec:authorize-acl="object :: permissions"
renders the element children (tag content) if the authenticated user has the specified permissions on the specified domain object, according to Spring Source's Access Control List system.
Configuration
In order to use the thymeleaf-extras-springsecurity3 or thymeleaf-extras-springsecurity4 modules in our Spring MVC application, we will first need to configure our application in the usual way for Spring + Thymeleaf applications (TemplateEngine bean, template resolvers, etc.), and add the SpringSecurity dialect to our Template Engine so that we can use the sec:*
attributes and special expression utility objects:
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> ... <property name="additionalDialects"> <set> <!-- Note the package would change to 'springsecurity3' if you are using that version --> <bean class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect"/> </set> </property> ... </bean>
And that's all!
Using the expression utility objects
The #authentication
object can be easily used, like this:
<div th:text="${#authentication.name}"> The value of the "name" property of the authentication object should appear here. </div>
The #authorization
object can be used in a similar way, normally in th:if
or th:unless
tags:
<div th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}"> This will only be displayed if authenticated user has role ROLE_ADMIN. </div>
The #authorization
object is an instance of org.thymeleaf.extras.springsecurity[3|4].auth.Authorization
, see this class and its documentation to understand all the methods offered.
Using the attributes
Using the sec:authentication
attribute is equivalent to using the #authentication
object, but using its own attribute:
<div sec:authentication="name"> The value of the "name" property of the authentication object should appear here. </div>
The sec:authorize
and sec:authorize-expr
attributes are exactly the same. They work equivalently to a th:if
that evaluated an #authorization.expression(...)
expression, by evaluating a Spring Security Expression:
<div sec:authorize="hasRole('ROLE_ADMIN')"> This will only be displayed if authenticated user has role ROLE_ADMIN. </div>
These Spring Security Expressions in sec:authorize
attributes are in fact Spring EL expressions evaluated on a SpringSecurity-specific root object containing methods such as hasRole(...)
, getPrincipal()
, etc.
As with normal Spring EL expressions, Thymeleaf allows you to access a series of objects from them including the context variables map (the #vars
object). In fact, you are allowed to surround your access expression with ${...}
if it makes you feel more comfortable:
<div sec:authorize="${hasRole(#vars.expectedRole)}"> This will only be displayed if authenticated user has a role computed by the controller. </div>
Remember that Spring Security sets a special security-oriented object as expression root, which is why you would not be able to access the expectedRole
variable directly in the above expression.
Another way of checking authorization is sec:authorize-url
, which allows you to check whether a user is authorized to visit a specific URL or not:
<div sec:authorize-url="/admin"> This will only be displayed if authenticated user can call the "/admin" URL. </div>
For specifying a specific HTTP method, do:
<div sec:authorize-url="POST /admin"> This will only be displayed if authenticated user can call the "/admin" URL using the POST HTTP method. </div>
Finally, there is an attribute for checking authorization using Spring Security's Access Control Lists, which needs the specification of a domain object and the permissions defined on it that we are asking for.
<div sec:authorize-acl="${obj} :: '1,3'"> This will only be displayed if authenticated user has permissions "1" and "3" on domain object referenced by context variable "obj". </div>
In this attribute, both domain object and permission specifications are considered to be thymeleaf Standard Expressions.
Namespace
The namespace for both Spring 3 and 4 versions of this dialect is http://www.thymeleaf.org/extras/spring-security
.
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
Getting the namespace incorrect won't impact processing of your template. It might however impact your IDE when it comes to things like suggestions/auto-completion in your templates.
https://github.com/thymeleaf/thymeleaf-extras-springsecurity
https://github.com/ultraq/thymeleaf-layout-dialect
http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#including-template-fragments
https://github.com/zsl131/thymeleaf-study
https://github.com/kolorobot/spring-boot-thymeleaf
http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-use-thymeleaf-3
https://github.com/spring-projects/spring-boot/blob/v1.4.1.RELEASE/spring-boot-samples/spring-boot-sample-web-thymeleaf3/src/main/resources/application.properties
http://blog.imyxiao.com/2016/11/26/Spring-Boot-thymeleaf-3/
https://github.com/kolorobot/spring-boot-thymeleaf
在spring-boot1.4之后,支持thymeleaf3,可以更改版本號來進行修改支持.
3相比2極大的提高了效率,並且不需要標簽閉合,類似的link,img等都有了很好的支持,按照如下配置即可
解決下面的問題的辦法是升級到Thymeleaf3
74.9 Use Thymeleaf 3
By default, spring-boot-starter-thymeleaf
uses Thymeleaf 2.1. If you are using the spring-boot-starter-parent
, you can use Thymeleaf 3 by overriding thethymeleaf.version
and thymeleaf-layout-dialect.version
properties, for example:
<properties> <thymeleaf.version>3.0.7.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version> <thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity4.version> </properties>
Tips:
如果不同時更改thymeleaf-extras-springsecurity4的version,則 thymeleaf spring security4相關的標簽會不生效
![]() |
if you are managing dependencies yourself, look at |
To avoid a warning message about the HTML 5 template mode being deprecated and the HTML template mode being used instead, you may also want to explicitly configure spring.thymeleaf.mode
to be HTML
, for example:
spring.thymeleaf.mode: HTML
Please refer to the Thymeleaf 3 sample to see this in action.
If you are using any of the other auto-configured Thymeleaf Extras (Spring Security, Data Attribute, or Java 8 Time) you should also override each of their versions to one that is compatible with Thymeleaf 3.0.
http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-use-thymeleaf-3
It's much simpler, just read this: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-use-thymeleaf-3
<properties> <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version> </properties>
You might also want to add <thymeleaf-extras-java8time.version>3.0.0.RELEASE</thymeleaf-extras-java8time.version> – Samuel EUSTACHI Jun 6 at 9:42
https://stackoverflow.com/questions/37439369/spring-boot-and-thymeleaf-3-0-0-release-integration
http://blog.imyxiao.com/2016/11/26/Spring-Boot-thymeleaf-3/
http://www.thymeleaf.org/doc/articles/thymeleaf3migration.html
https://github.com/spring-projects/spring-boot/tree/v1.4.1.RELEASE/spring-boot-samples/spring-boot-sample-web-thymeleaf3
org.xml.sax.SAXParseException: 元素類型 "img" 必須由匹配的結束標記 "</img>" 終止。
1、必須要有結束標簽,這個很煩,不加就報錯,像下面這樣:
org.xml.sax.SAXParseException: 元素類型 "input" 必須由匹配的結束標記 "</input>" 終止。
org.xml.sax.SAXParseException: 元素類型 "img" 必須由匹配的結束標記 "</img>" 終止。
org.xml.sax.SAXParseException: 元素類型 "hr" 必須由匹配的結束標記 "</hr>" 終止。
org.xml.sax.SAXParseException: 元素類型 "br" 必須由匹配的結束標記 "</br>" 終止。
2、包含的模板,也必須有閉合標簽。我要引入公共的頭部和底部,頭部還好說,外面有個<head>包着,底部必須得在外面套個div,引入js的代碼也得包括在div里,不爽啊,像這樣:
<div class="container" th:fragment="footer">
<hr></hr>
<div>
<p>AntsClub 2013-2015</p>
</div>
<script th:src="@{bootstrap334/js/jquery.min.js}"></script>
<script th:src="@{bootstrap334/js/bootstrap.min.js}"></script>
</div>
3、引入個css文件,js文件也得用thymeleaf的標簽,就像上面的例子
如果你的代碼使用了 HTML5 的標准,而Thymeleaf 版本來停留在 2.x ,那么如果沒有把<input>
閉合,如下:
<form>
First name:<br>
<input type="text" name="firstname">
<br>
Last name:<br>
<input type="text" name="lastname">
</form>
就會拋出如下錯誤。
org.xml.sax.SAXParseException: 元素類型 "input" 必須由匹配的結束標記 "</input>" 終止。
解決方案
1. 沿用 Thymeleaf 老版本
如果你的 Thymeleaf 不能變更,那么你的 HTML 標准也只能停留在老版本了。你必須嚴格遵守 XML 定義,在<input>
加上結束標記</input>
。這顯然,對於 HTML5 不友好。
2. 升級至 Thymeleaf 3 新版本
是時候嘗試下使用 Thymeleaf 3 了。Thymeleaf 3 使用了新的解析系統。
Thymeleaf 3 不再是基於XML結構的。由於引入新的解析引擎,模板的內容格式不再需要嚴格遵守XML規范。即不在要求標簽閉合,屬性加引號等等。當然,出於易讀性考慮,還是推薦你按找XML的標准去編寫模板。
Thymeleaf 3 使用一個名為 AttoParser 2的新解析器。 一個新的、基於事件(不符合SAX標准)的解析器,AttoParser由 Thymeleaf 的作者開發,符合 Thymeleaf 的風格。
AttoParser 提供 Thymeleaf 3 兩個重要功能:
- 完全支持XML和HTML5(非XML化)標記,從而不再需要外部標記平衡操作。
- 無損解析,以便在處理的輸出的標記類似於具有最高精度的原始模板。
所以下面的格式在 Thymeleaf 3 里面是合法的:
<div><img alt=logo th:src='@{/images/logo.png}'>
Thymeleaf 3 其他方面的解析改進
1. 啟用驗證的解析
在 Thymeleaf 2.1提供了兩種VALID*
模板模式,名為VALIDXHTML
和VALIDXML
,在而 Thymeleaf 3 中將不再存在。 新的解析基礎結構不提供HTML或XML驗證,即在解析期間無法驗證模板標記是否符合指定的DTD或XML模式定義。
2. 不再需要<![CDATA[ ... ]]>
Thymeleaf 2.1 要求將<script>
標記的內容封裝在 CDATA 中,以便所使用的任何<
或>
符號不會干擾基於XML的解析:
<script>
/*<![CDATA[*/
var user = ...
if (user.signupYear < 1990) {
alert('You\'ve been here for a long time!');
}
/*]]>*/
</script>
而在 Thymeleaf 3 中則不需要這樣做,代碼立馬變得簡潔干凈:
<script>
var user = ...
if (user.signupYear < 1990) {
alert('You\'ve been here for a long time!');
}
</script>
參考文獻
- https://github.com/thymeleaf/thymeleaf/issues/390
https://waylau.com/thymeleaf-3-adopts-a-new-parsing-system/
THYMELEAF 3 - GET STARTED QUICKLY WITH THYMELEAF 3 AND SPRING MVC
Thymeleaf 3 release arrived. The new version brings plenty of new features like HTML5 support as well as Text templates support with no markup - [# th:utext="${thymeleaf.version}" /]
, improved inline capabilities - <p>Thymeleaf [[${thymeleaf.version}]] is great!</p>
, performence improvements and much more.
The easiest way the get starter with Thymeleaf 3 and Spring MVC is by using Spring MVC 4 Quickstart Maven Archetype. The archetype was updated to support Thymeleaf 3. The changes that are made to the archetype are described below.
Dependencies
The project uses Spring Platform BOM for dependencies management, but it does not yet (as time of writing this post) declare dependency on Thymeleaf 3, so I needed to declare the versions manually.
- Thymeleaf:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.0.RELEASE</version> </dependency>
- Thymeleaf Spring 4:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>3.0.0.RELEASE</version> </dependency>
- Thymeleaf Spring Security 4:
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.0.RELEASE</version> </dependency>
The application generated with the archetype uses Java 8 Time Dialect and since Thymeleaf API changed, the dialect dependency must be updated too. Before it is available in Maven Central, we must add snapshot repository to POM:
<repository> <id>sonatype-nexus-snapshots</id> <name>Sonatype Nexus Snapshots</name> <url>https://oss.sonatype.org/content/repositories/snapshots</url> <snapshots> <enabled>true</enabled> </snapshots> </repository>
And then declare the dependency:
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> <version>3.0.0-SNAPSHOT</version> </dependency>
Configuration changes
- Template resolver
Template resolver before:
@Bean public TemplateResolver templateResolver() { TemplateResolver resolver = new ServletContextTemplateResolver(); resolver.setPrefix(VIEWS); resolver.setSuffix(".html"); resolver.setTemplateMode("HTML5"); resolver.setCacheable(false); return resolver; }
Template resolver after:
@Bean public ITemplateResolver templateResolver() { SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); resolver.setPrefix(VIEWS); resolver.setSuffix(".html"); resolver.setTemplateMode(TemplateMode.HTML); resolver.setCacheable(false); return resolver; }
- Template Engine
@Bean public SpringTemplateEngine templateEngine() { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); templateEngine.addDialect(new SpringSecurityDialect()); templateEngine.addDialect(new Java8TimeDialect()); return templateEngine; }
- View Resolver:
@Bean public ViewResolver viewResolver() { ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver(); thymeleafViewResolver.setTemplateEngine(templateEngine()); thymeleafViewResolver.setCharacterEncoding("UTF-8"); return thymeleafViewResolver; }
Templates
The templates did not change in this project. But if you are migrating a real project, you may be interested in reading migration guide.
References
You may be also interested in
http://blog.codeleak.pl/2016/05/thymeleaf-3-get-started-quickly-with.html