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/template 和 strings。模板文件可以使用 ParseFS 从嵌入文件系统加载;解析通常只做一次并缓存,已解析模板可被多个 goroutine 并发执行,但输出 writer 仍需由调用方保证并发安全。
复杂业务逻辑应放在准备数据的 Go 代码中,而不是把模板写成业务程序。模板来自配置或用户输入时必须限制来源;不可信用户不能直接控制模板语法或函数集合。
安全边界
html/template 会结合 HTML 上下文转义文本、属性、URL 和脚本位置。不要使用 template.HTML、template.URL 等安全类型包裹未经验证的输入,否则会绕过自动转义。纯文本邮件或配置输出才使用 text/template。
常见错误
- 用
text/template渲染 HTML,造成 XSS 风险。 - 忽略
Parse或Execute错误,输出被截断却返回成功。 - 每次请求重新解析模板,造成不必要的 I/O 和 CPU。
- 把
template.HTML等安全类型用于未经验证的输入。 - 让不可信用户控制模板语法或函数集合。
前端模板也会转义变量,但 Go 的 html/template 会结合 HTML 上下文处理 URL、属性和脚本位置;不要把它当作简单字符串替换。
小结
- HTML 使用
html/template,纯文本使用text/template。 - 解析与执行分离,解析后的模板可复用并发执行。
- 保持模板逻辑简单,严格控制函数、模板来源和安全类型。