跳到主要内容

Template Engine(模板引擎)

模板把数据渲染为文本。text/template 适合纯文本,html/template 会根据 HTML 上下文自动转义,Web 页面应优先使用后者。

基本渲染

package main

import (
"html/template"
"os"
)

func main() {
tmpl, err := template.New("page").Parse(`<h1>{{.Title}}</h1>`)
if err != nil {
panic(err)
}
data := struct{ Title string }{Title: "Hello"}
if err := tmpl.Execute(os.Stdout, data); err != nil {
panic(err)
}
}

{{.Field}} 访问当前数据,管道可以串联函数,例如 {{.Name | printf "%q"}}。解析错误应在启动或构建阶段暴露;执行错误必须返回给调用方,不能把截断输出当作成功。

函数与模板生命周期

使用 Funcs 注册函数后再 Parse

tmpl := template.Must(template.New("mail").Funcs(template.FuncMap{
"upper": strings.ToUpper,
}).Parse(`{{upper .Name}}`))

片段需要导入 html/templatestrings。模板文件可以使用 ParseFS 从嵌入文件系统加载;解析通常只做一次并缓存,已解析模板可被多个 goroutine 并发执行,但输出 writer 仍需由调用方保证并发安全。

复杂业务逻辑应放在准备数据的 Go 代码中,而不是把模板写成业务程序。模板来自配置或用户输入时必须限制来源;不可信用户不能直接控制模板语法或函数集合。

安全边界

html/template 会结合 HTML 上下文转义文本、属性、URL 和脚本位置。不要使用 template.HTMLtemplate.URL 等安全类型包裹未经验证的输入,否则会绕过自动转义。纯文本邮件或配置输出才使用 text/template

常见错误

  • text/template 渲染 HTML,造成 XSS 风险。
  • 忽略 ParseExecute 错误,输出被截断却返回成功。
  • 每次请求重新解析模板,造成不必要的 I/O 和 CPU。
  • template.HTML 等安全类型用于未经验证的输入。
  • 让不可信用户控制模板语法或函数集合。

前端模板也会转义变量,但 Go 的 html/template 会结合 HTML 上下文处理 URL、属性和脚本位置;不要把它当作简单字符串替换。

小结

  • HTML 使用 html/template,纯文本使用 text/template
  • 解析与执行分离,解析后的模板可复用并发执行。
  • 保持模板逻辑简单,严格控制函数、模板来源和安全类型。