Go的text/template
、html/template
包用于处理使用字符串模板和数据驱动生成目标字符串,并可以在字符串模板中使用显示数据、流程控制、函数、管道、子模板等功能。
介绍
Template 结构体
- New 创建目标
- Parse 解析模板字符串
- ParseFiles 解析文件模板
- ParseGlob 解析Glob文件模板
- Must 对模板创建结果验证
- Execute 渲染模板
- ExecuteTemplate 指定渲染模板
- Funcs 自定义函数
- Clone 复制模板进行模板复用
预定义函数
参考
- 逻辑运算:
and
、or
、not
- 比较运算:
eq
、ne
、lt
、le
、gt
、ge
- 组合模板函数:
define
、template
、block
- 调用操作:
call
- 数组:
index
、len
- 格式化操作:
print
、printf
、println
,与用直接调用 fmt.Sprint/Sprintf/Sprintln
功能一致
- 转义:
html
、js
、urlquery
函数对字符串进行转义,防止安全问题
示例
数据显示
package main
import (
"fmt"
ht "html/template"
"os"
tt "text/template"
)
func main() {
// 显示数据
tplText := `hello golang {{ . }}`
ttpl, err := tt.New("ttpl").Parse(tplText)
if err != nil {
fmt.Println(err)
} else { // hello golang <a href="https://www.xiexianbin.cn">blog</a>
ttpl.Execute(os.Stdout, `<a href="https://www.xiexianbin.cn">blog</a>`)
fmt.Println()
}
htpl, err := ht.New("htpl").Parse(tplText)
if err != nil {
fmt.Println(err)
} else { // hello golang <a href="https://www.xiexianbin.cn">blog</a><nil>
fmt.Println(htpl.Execute(os.Stdout, `<a href="https://www.xiexianbin.cn">blog</a>`))
fmt.Println()
}
}
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
//tplText := `hello golang {{ . }`// 错误演示
tplText := `hello golang {{ . }}`
tpl := template.Must(template.New("tpl").Parse(tplText)) // 直接输出错误
tpl.Execute(os.Stdout, `html/template`)
fmt.Println()
// slice
tplText = `hello golang {{ index . 0 }}`
tpl = template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, []string{"html/template", "golang"})
fmt.Println()
// map
tplText = `hello golang {{ .htmltemplate }} {{ .key1 }}`
tpl = template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, map[string]string{"htmltemplate": "html/template", "key1": "val1"})
fmt.Println()
// struct
tplText = `hello golang {{ .Name }} {{ .Age }}`
tpl = template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, struct {
Name string
Age int
}{
Name: "xianbin",
Age: 18,
})
}
条件语句
package main
import (
"html/template"
"os"
)
func main() {
// struct 条件语句
tplText := `hello golang {{ .Name }} {{ .Age }} {{ if eq .Sex 1 }}男{{ else }}女{{ end }}`
tpl := template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, struct {
Name string
Age int
Sex int
}{
Name: "xianbin",
Age: 18,
Sex: 1,
})
}
遍历
package main
import (
"html/template"
"os"
)
func main() {
tplText := `hello golang {{ range . }} {{ .Name }} {{ .Age }} {{ end }}`
tpl := template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, []struct {
Name string
Age int
}{{
Name: "xianbin",
Age: 18,
}, {
Name: "x",
Age: 19,
}})
}
自定义函数
template.FuncMap()
- key 字符串,函数名,需要在模板中调用
- value 函数
package main
import (
"html/template"
"os"
"strings"
)
func main() {
tplText := `hello golang {{ upper . }} {{ title . }}`
funcs := template.FuncMap{
"upper": strings.ToUpper,
"title": func(str string) string {
if len(str) == 0 {
return ""
} else if len(str) == 1 {
return strings.ToUpper(str)
}
return strings.ToUpper(str[:1]) + str[1:]
},
}
// 模板函数必须在解析模板前加载
tpl := template.Must(template.New("tpl").Funcs(funcs).Parse(tplText))
tpl.Execute(os.Stdout, "abcde")
}
模板替代
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
tplText := `hello golang {{ block "content" . }} {{ . }} {{ end }}`
tpl := template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, "abcde")
fmt.Println()
// 替代block
tpl2 := template.Must(template.New("tpl2").Parse(tplText))
tpl2, err := tpl2.Parse(`{{ define "content" }}len {{ len . }} {{ end }}`)
fmt.Println(err, tpl2.Name())
tpl2.Execute(os.Stdout, "abcde")
fmt.Println()
}
模板嵌入
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
tplText := `{{- define "len" }} {{ len . }} {{ end }}
{{- define "raw" }} {{ . }} {{ end }}
{{ template "len" . }}
{{ template "raw" . }}`
tpl := template.Must(template.New("tpl").Parse(tplText))
tpl.Execute(os.Stdout, "abcde")
fmt.Println()
// 调用指定的方法,替代模板中的 template 指定的模板
tpl.ExecuteTemplate(os.Stdout, "raw", "abcde")
fmt.Println()
// Output:
// 5
// abcde
// abcde
}
ParseFiles 示例
$ cat 1.html
{{ range . }}
{{ . }}
{{ end }}
{{ template "2.html" . }}
$ cat 2.html
{{ len . }}
package main
import (
"html/template"
"os"
)
func main() {
tpl := template.Must(template.ParseFiles("1.html", "2.html"))
tpl.ExecuteTemplate(os.Stdout, "1.html", []string{"a", "b"})
tpl.ExecuteTemplate(os.Stdout, "2.html", "abcd")
}
ParseGlob 示例
package main
import (
"html/template"
"os"
)
func main() {
tpl := template.Must(template.ParseGlob("*.html"))
tpl.ExecuteTemplate(os.Stdout, "1.html", []string{"a", "b"})
tpl.ExecuteTemplate(os.Stdout, "2.html", "abcd")
}
html 模型示例
$ cat views/list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>users</title>
</head>
<body>
{{ range . }}
{{ .Name }} - {{ .GetName }} <br>
{{ end }}
</body>
</html>
$ cat add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form name="add" method="post" action="/city/add/">
名称:<input name="name" type="text" /><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
package main
import (
"log"
"net/http"
"text/template"
)
type City struct {
Name string
//NameText func() string // 定义函数,可以使用 call 调用
}
func (c City) GetName() (string, error) {
return c.Name, nil
}
var cities = []*City{
&City{
Name: "shanghai",
},
&City{
Name: "zhengzhou",
},
&City{
Name: "shangqiu",
},
}
func GetCity() []*City {
return cities
}
func AddCity(name string) {
cities = append(cities, &City{
Name: name,
})
}
func main() {
addr := ":8080"
http.HandleFunc("/city/", func(w http.ResponseWriter, req *http.Request) {
tpl := template.Must(template.ParseFiles("views/list.html"))
tpl.ExecuteTemplate(w, "list.html", GetCity())
})
http.HandleFunc("/city/add/", func(w http.ResponseWriter, request *http.Request) {
if request.Method == http.MethodGet {
tpl := template.Must(template.ParseFiles("views/add.html"))
tpl.Execute(w, nil)
} else if request.Method == http.MethodPost {
log.Println(request.ParseForm())
name := request.FormValue("name")
log.Println("name is", name)
AddCity(name)
http.Redirect(w, request, "/city/", 302)
}
return
})
log.Println("listen on", addr)
log.Fatal(http.ListenAndServe(addr, nil))
}