Go text/template 是 Go 语言标准库中的一个模板引擎,用于生成文本输出。它使用类似于 HTML 的模板语言,可以将数据和模板结合起来,生成最终的文本输出。
Go html/template包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。
它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用html/template这个包。
1 使用方法
html/template 为go的内置包直接 import “html/template” 即可,模板引擎的使用一般三个步骤,定义,解析,渲染。
模板语法都包含在 {{和}} 中间,其中 {{.}} 中的点表示当前对象。对象可以是变量、内置变量、控制结构、内部函数、自定义函数,注释等等。
2 从Hello World开始
2.1 创建一个html模板文件
模板文件名为 hello,html,内容如下:
{{.}}
2.2 解析模板并输出到浏览器
// test3 project main.go
package main
import (
"net/http"
"html/template"
)
func main() {
http.HandleFunc("/", hello)
println("打开浏览器试试 http://localhost:5217")
err := http.ListenAndServe(":5217", nil)
if err != nil {
println("HTTP server start failed! err : ", err)
return
}
}
// hello 函数把解析后的文件输出到html
func hello(w http.ResponseWriter, r *http.Request) {
s := "Hello World!"
t, _ := template.ParseFiles("hello.html")
t.Execute(w, s)
}
运行一下:
浏览器查看:
是不是很简单啊。
3 一个综合使用的例子
这个例子包括解析结构体,map,内置变量,注释,内置函数,自定义函数等。
3.1 创建一个html模板
其实文件名叫啥都是,比如html.tmpl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>golang html demo</title>
</head>
<body>
<div>
<p>王子:</p>
<p>Hello {{.m1.name}}</p>
<p>年龄: {{.m1.age}}</p>
<p>性别: {{.m1.gender}}</p>
<p>公主:</p>
{{/* with 省略写法 */}}
{{with .u1}}
<p>Hello {{ .Name}}</p>
<p>年龄: {{ .Age}}</p>
<p>性别: {{ .Gender}}</p>
{{end}}
</div>
<div>
<p>内置变量:</p>
{{/* 定义内置变量*/}}
{{$i := 100}}
{{$x := .u1.Age}}
{{/* 在页面显示内置变量*/}}
{{$i}}
{{$x}}
</div>
<div>
<p>内置函数:</p>
{{/*内置函数*/}}
{{$a := 0}}
{{$b := 1}}
{{or $a $b}}
{{and $a $b}}
{{len .m1}}
<p>比较运算:</p>
{{eq $a $b}}
</div>
</body>
</html>
3.2 Go代码
// 模板只能传入一个变量,多个变量需要组合到 map里
package main
import (
"fmt"
"html/template"
"net/http"
)
type User struct {
Name string
Gender string
Age int
}
func index(w http.ResponseWriter, r *http.Request) {
// 解析模板
t, err := template.ParseFiles("html.tmpl")
if err != nil {
fmt.Println("Parse template failed! err:", err)
return
}
// 渲染模板
u1 := User{
Name: "小公主",
Gender: "女",
Age: 16,
}
m1 := map[string]interface{}{
"name": "小皇子",
"gender": "男",
"age": 18,
}
err = t.Execute(w, map[string]interface{}{
"u1": u1,
"m1": m1,
})
if err != nil {
fmt.Println("render template failed,err : ", err)
return
}
}
func diyifun(w http.ResponseWriter, r *http.Request) {
// 自定义函数
// 1 自定义函数变量
wenfun := func(name string) (string, error) {
return name + " 您今儿吃了吗?", nil
}
// 2 创建一个名称为diyfun的模板对象,要求名称跟文件名一样,扩展名也是
t := template.New("diyfun.tmpl")
// 3 在解析模板之前注册函数
t.Funcs(template.FuncMap{
"wen": wenfun,
})
// 4 解析模板
_, err := t.ParseFiles("diyfun.tmpl")
if err != nil {
fmt.Println("Parsetemplate failed! err:", err)
return
}
// 渲染模板
t.Execute(w, "白龙马")
}
func main() {
http.HandleFunc("/index", index)
http.HandleFunc("/diyifun", diyifun)
println("打开浏览器试试 http://localhost:5217/index")
err := http.ListenAndServe(":5217", nil)
if err != nil {
fmt.Println("HTTP server start failed! err : ", err)
return
}
}
3.3 打开浏览器看渲染效果
-
index 综合
-
diyifun 自定义函数
4 更复杂的嵌套模板
就是在一个模板中嵌套另外的一个模板。
在需要嵌套的地方
{{ template 模板名 . }}
其中,这个 . 是在模板中传递数据用的
4.1 先看看模板
- Home.tmpl
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>嵌套2个子模板</title>
</head>
<body>
<p>这是子模板1:</p>
ul :<br>
{{ template "tmp1.tmpl" . }}
<hr>
<p>这是子模板1:</p>
ol :<br>
{{ template "tmp2.tmpl" . }}
</body>
</html>
- tmp1.tmpl
<ul>
<li>{{ .name }}</li>
<li>{{ .sex }}</li>
</ul>
- tmp2.tmpl
<ol>
<li>{{ .name }}</li>
<li>{{ .sex }}</li>
</ol>
4.2 主控程序
// test3 project main.go
package main
import (
"fmt"
"html/template"
"net/http"
)
func main() {
http.HandleFunc("/home", home)
println("打开浏览器试试 http://localhost:5217/home")
err := http.ListenAndServe(":5217", nil)
if err != nil {
println("HTTP server start failed! err : ", err)
return
}
}
// hello 函数把解析后的文件输出到html
func home(w http.ResponseWriter, r *http.Request) {
// s := "Hello World!"
t, err := template.ParseFiles("home.tmpl", "tmp1.tmpl", "tmp2.tmpl")
if err != nil {
fmt.Printf("parse file failed err := %v", err)
}
mp := map[string]interface{}{
"name": "白龙马",
"sex": "男",
}
err = t.Execute(w, mp)
if err != nil {
fmt.Printf("execute file failed err := %v", err)
}
}
4.3 运行效果
5 block定义一个默认模板
Home.tmpl 修改一下,嵌入一个不存在的模板tmp3.tmpl,如果直接 {{ template “tmp3.tmpl” . }}嵌入编译时就会报错,用block定义一个默认模板就OK:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>嵌套2个子模板</title>
</head>
<body>
<p>这是子模板1:</p>
ul :<br>
{{ template "tmp1.tmpl" . }}
<hr>
<p>这是子模板2:</p>
ol :<br>
{{ template "tmp2.tmpl" . }}
<hr>
<p>这是子模板3:</p>
default :<br>
{{ block "tmp3.tmpl" . }}
tmp3.tmpl 没找到,这是默认模板的内容
{{end}}
</body>
</html>
运行效果如下:
现在新建一个tmp3.tmpl的模板:
别搞错了,我才是Tmp3.tmpl
<br>
{{ .name}}
{{ .sex}}
模板解析采用统配符解析函数:
t, err := template.ParseGlob(“nest/*.tmpl”)
解析 nest目录下所有的*.tmpl文件
刷新一下浏览器,默认模板替换为了tml3.tmpl
6 还尝试了其它方法
比如条件渲染,循环结构等。
package main
import (
"fmt"
"html/template"
"net/http"
)
type Book struct {
Title string
Publisher string
Year int
}
func main() {
http.HandleFunc("/index2", index2)
err := http.ListenAndServe(":5217", nil)
if err != nil {
fmt.Println("HTTP server start failed! err : ", err)
return
}
}
func index2(w http.ResponseWriter, r *http.Request) {
// // 解析模板
// t, err := template.ParseFiles("index.html")
// if err != nil {
// fmt.Println("Parse template failed! err:", err)
// return
// }
// 字符串
t1 := template.New("Template")
t1, _ = t1.Parse("External variable has the value [{{.}}]\n")
t1.Execute(w, "Amazing")
// 结构体
b := Book{"The CSound Book", "MIT Press", 2002}
t1.Execute(w, b)
// 结构体字段
t2 := template.New("Template")
t2, _ = t2.Parse("External variable Book has the values [Title: {{.Title}}, Publisher: {{.Publisher}}, Year: {{.Year}}]\n")
t2.Execute(w, b)
// 内部变量 $前缀
t, _ := template.New("Template").Parse("{{$var:=2150}}Internal variable has the value [{{$var}}]\n")
t.Execute(w, nil)
// 条件渲染
/*
等于 eq(对应 ==):
非等于 ne(对应 !=)
小于 lt(对应 <)
小于等于 le(对应 <=)
大于 gt(对应 >)
大于等于 ge(对应 >=)
*/
t, err := template.New("Template").Parse("{{if eq . `filler`}}This is filler...{{else}}It's something else...{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(w, "filler")
// 直接写入布尔变量,而不是比较语句。
t, _ = template.New("Template").Parse("{{if .}}This is true.{{else}}This is false.{{end}}\n")
t.Execute(w, false)
// 循环遍历
computerList := []string{"Arduino", "Raspberri Pi", "NVidia Jetson Nano"}
t, err = template.New("Template").Parse("My favorite computers are:\n{{range .}}{{.}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(w, computerList)
// 有序列表
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
t, err = template.New("Template").Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{$index}}) {{$item}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(w, dishesList)
// 模板中使用函数
//dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
t, err = template.New("Template").Funcs(template.FuncMap{"add": add}).Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{add $index 1}}) {{$item}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(w, dishesList)
//dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
tmpl := "Index{{dl}}Dish\n{{range $index, $item:=.}}{{add $index 1}}{{dl}}{{$item}}\n{{end}}\n"
funcMap := template.FuncMap{"add": add, "dl": delimiter(",")}
t, _ = template.New("Template").Funcs(funcMap).Parse(tmpl)
t.Execute(w, dishesList)
}
// 模板中使用函数add
func add(a, b int) int {
return a + b
}
// 这只分割符
func delimiter(s string) func() string {
return func() string {
return s
}
}
运行结果: