Golang的text/template库用于将数据注入文本模板,适用于生成配置文件、邮件等非HTML内容,而html/template会自动转义HTML字符以防止XSS攻击,适合Web页面输出;选择时应根据输出类型决定,非HTML用text/template,HTML则用html/template。

Golang的
text/template
使用
text/template
首先,你需要一个模板字符串,它包含了静态文本和用于动态插入数据的“动作”(actions)。这些动作通常用双大括号
{{...}}package main
import (
"log"
"os"
"text/template"
)
func main() {
// 1. 定义模板字符串
// 这里的.Name和.Age是占位符,对应传入数据结构的字段
templateString := `你好,{{.Name}}!你今年{{.Age}}岁了。
希望你喜欢这个简单的模板示例。`
// 2. 准备要注入模板的数据
// 通常是一个结构体或map
type User struct {
Name string
Age int
}
userData := User{
Name: "张三",
Age: 30,
}
// 3. 解析模板
// template.New("name") 创建一个新模板,"name"是模板的标识符
// .Parse(templateString) 解析模板字符串
tmpl, err := template.New("greeting").Parse(templateString)
if err != nil {
log.Fatalf("模板解析失败: %v", err)
}
// 4. 执行模板并输出结果
// .Execute(io.Writer, data) 将数据应用到模板,并将结果写入指定的io.Writer
// os.Stdout 是标准输出
err = tmpl.Execute(os.Stdout, userData)
if err != nil {
log.Fatalf("模板执行失败: %v", err)
}
// 复杂一点的例子:处理列表
usersData := struct {
Users []User
}{
Users: []User{
{Name: "李四", Age: 25},
{Name: "王五", Age: 35},
},
}
listTemplateString := `用户列表:
{{range .Users}} - {{.Name}} ({{.Age}}岁)
{{end}}`
listTmpl, err := template.New("userList").Parse(listTemplateString)
if err != nil {
log.Fatalf("列表模板解析失败: %v", err)
}
log.Println("\n--- 列表示例 ---")
err = listTmpl.Execute(os.Stdout, usersData)
if err != nil {
log.Fatalf("列表模板执行失败: %v", err)
}
}这段代码展示了
text/template
Execute
io.Writer
os.Stdout
bytes.Buffer
立即学习“go语言免费学习笔记(深入)”;
text/template
html/template
这个问题经常被问到,也是我刚接触Go模板时有些困惑的地方。简单来说,
text/template
html/template
html/template
html/template
<h1>Hello</h1>
html/template
<h1>Hello</h1>
而
text/template
如何选择?
我的经验是,遵循一个简单的原则:
html/template
html/template
text/template
举个例子,如果你要生成一个Nginx的配置文件,里面有路径、端口号等,用
text/template
/
/
html/template
// html/template 示例
package main
import (
"html/template"
"log"
"os"
)
func main() {
// 包含HTML标签和潜在的JS代码
dangerousInput := `<h1>Hello World!</h1><script>alert('XSS Attack!');</script>`
tmpl, err := template.New("html_test").Parse(`<div>{{.Content}}</div>`)
if err != nil {
log.Fatal(err)
}
log.Println("--- html/template 转义示例 ---")
err = tmpl.Execute(os.Stdout, struct{ Content string }{Content: dangerousInput})
if err != nil {
log.Fatal(err)
}
// 输出会是:<div><h1>Hello World!</h1><script>alert('XSS Attack!');</script></div>
}可以看到,
html/template
text/template
处理复杂的数据结构和引入自定义函数是
text/template
处理复杂的嵌套数据结构:
模板引擎通过点
.
.
dot
range
with
假设我们有这样的数据结构:
type Item struct {
Name string
Quantity int
Price float64
}
type Order struct {
OrderID string
Customer struct {
Name string
Email string
}
Items []Item
TotalAmount float64
}我们可以这样在模板中访问:
orderData := Order{
OrderID: "20230815-001",
Customer: struct {
Name string
Email string
}{
Name: "王小明",
Email: "xiaoming@example.com",
},
Items: []Item{
{Name: "Go语言编程", Quantity: 1, Price: 89.90},
{Name: "机械键盘", Quantity: 1, Price: 599.00},
},
TotalAmount: 688.90,
}
templateString := `订单号: {{.OrderID}}
客户信息:
姓名: {{.Customer.Name}}
邮箱: {{.Customer.Email}}
订单详情:
{{range .Items}}
- {{.Name}} (数量: {{.Quantity}}, 单价: {{.Price | printf "%.2f"}})
{{end}}
总金额: {{.TotalAmount | printf "%.2f"}}`
tmpl, err := template.New("order").Parse(templateString)
if err != nil {
log.Fatalf("解析失败: %v", err)
}
log.Println("\n--- 复杂数据结构示例 ---")
err = tmpl.Execute(os.Stdout, orderData)
if err != nil {
log.Fatalf("执行失败: %v", err)
}在这个例子中,
{{.Customer.Name}}Order
Customer
Name
{{range .Items}}Items
.
Item
{{.Name}}{{.Quantity}}Item
自定义函数(FuncMap
有时候,模板中需要执行一些逻辑,比如格式化日期、字符串操作、数学计算等,这些Go语言的内置函数无法直接提供。这时,你可以通过
template.FuncMap
FuncMap
map[string]interface{}error
package main
import (
"fmt"
"log"
"os"
"strings"
"text/template"
"time"
)
// 定义一个将字符串转换为大写的函数
func toUpper(s string) string {
return strings.ToUpper(s)
}
// 定义一个格式化日期的函数
func formatDate(t time.Time, format string) string {
return t.Format(format)
}
// 定义一个计算两个数之和的函数
func add(a, b int) int {
return a + b
}
func main() {
// 创建FuncMap,将自定义函数注册进去
funcMap := template.FuncMap{
"upper": toUpper,
"fdate": formatDate,
"add": add,
}
templateString := `
用户名: {{.UserName | upper}}
当前日期: {{fdate .CurrentTime "2006-01-02 15:04:05"}}
计算结果: 10 + 20 = {{add 10 20}}
`
data := struct {
UserName string
CurrentTime time.Time
}{
UserName: "john doe",
CurrentTime: time.Now(),
}
// 解析模板时,将FuncMap传递给New或Funcs方法
// 注意:Funcs方法必须在Parse之前调用
tmpl, err := template.New("custom_funcs").Funcs(funcMap).Parse(templateString)
if err != nil {
log.Fatalf("模板解析失败: %v", err)
}
log.Println("\n--- 自定义函数示例 ---")
err = tmpl.Execute(os.Stdout, data)
if err != nil {
log.Fatalf("模板执行失败: %v", err)
}
// 思考一下,如果函数签名不匹配会怎样?
// 比如,你定义了一个需要两个int参数的函数,但模板中只传了一个。
// 模板执行时会报错,提示参数数量不匹配。
// 同样,如果函数返回了error,模板执行也会中断并返回该error。
}通过
FuncMap
text/template
在使用
text/template
常见的坑:
nil
nil
panic
type Data struct {
User *struct{ Name string }
}
// data := Data{User: nil}
// template: {{.User.Name}} -> panic!解决方案: 在模板中使用
if
{{if .User}}{{.User.Name}}{{else}}匿名用户{{end}}或者确保传入的数据结构不会出现
nil
数据类型不匹配或字段名错误: 模板期望某个类型的字段,但实际传入的数据类型不符,或者字段名拼写错误,模板通常会静默地输出空字符串,这在调试时可能让人摸不着头脑。
// 数据中没有名为 "Username" 的字段,只有 "Name"
// template: 你好,{{.Username}}
// 结果: 你好,解决方案: 仔细检查模板中的字段名与Go结构体字段名是否一致(注意大小写,Go模板只能访问导出字段)。对于复杂的模板,单元测试是发现这类问题的有效手段。
上下文(dot
range
with
dot
range
$
// 假设数据结构中有 .GlobalConfig.AppName
// {{range .Items}}
// {{$.GlobalConfig.AppName}} - {{.Name}}
// {{end}}如果忘记
$
{{.GlobalConfig.AppName}}range
dot
Item
GlobalConfig
模板解析错误: 模板语法本身有误,比如括号不匹配、关键字拼写错误等。
template.Parse
template.ParseFiles
性能考量:
在高性能服务中,模板的使用方式对性能有显著影响。
预解析模板: 这是最重要的优化手段。绝对不要在每次请求时都重新解析模板。 模板的解析是一个相对耗时的操作(文件I/O、语法树构建等)。
*template.Template
map[string]*template.Template
var templates = make(map[string]*template.Template)
func init() { // 解析单个文件 tmpl, err := template.ParseFiles("templates/index.html") if err != nil { log.Fatalf("解析模板失败: %v", err) } templates["index"] = tmpl
// 或者解析多个文件,并命名主模板
// tmpl, err := template.ParseFiles("templates/base.html", "templates/header.html", "templates/footer.html")
// templates["base"] = tmpl
// 使用ParseGlob解析目录下的所有模板
// tmpl, err = template.ParseGlob("templates/*.html")
// templates["all"] = tmpl}
// 在处理请求时,直接通过名称获取并执行 func handler(w http.ResponseWriter, r *http.Request) { err := templates["index"].Execute(w, someData) // ... }
减少模板文件I/O: 如果模板文件很多,
ParseFiles
ParseGlob
数据量与复杂性: 传入模板的数据量越大、结构越复杂,模板执行的耗时也会相应增加。在极端情况下,可以考虑对数据进行预处理或简化,只将模板真正需要的数据传入。
避免在模板中进行复杂计算: 尽管可以通过
FuncMap
通过注意这些细节,你可以让
text/template
以上就是Golang text/template库文本模板生成与使用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号