必须用 html/template 渲染 HTML 内容,因其自动按上下文转义防止 XSS;text/template 仅适用于纯文本场景。

用 html/template 写页面,不是“写 HTML + 塞变量”,而是“在安全上下文中做数据驱动渲染”——不加区分地混用 text/template 和 html/template 是最常见、后果最严重的起点错误。
什么时候必须用 html/template 而非 text/template
只要输出目标是浏览器可解析的 HTML(哪怕只是片段、AJAX 返回的 <div>),就必须用 html/template。它会在插值时自动根据上下文转义:{{.Name}} 中含 <script> 会被转成 <script></script>;{{.URL}} 在 href="{{.URL}}" 中会做 URL 编码;{{.JSData}} 进入 onclick="do({{.JSData}})" 会做 JS 字符串转义。
用 text/template 渲染 HTML 页面,等于主动放弃 XSS 防御能力。即使你手动调用 html.EscapeString,也覆盖不了所有上下文(如 CSS 属性、JS 正则字面量、事件处理器等)。
- HTTP handler 返回 HTML 页面 → 必须
html/template - 生成邮件正文(HTML 格式)→ 必须
html/template - 生成 Nginx 配置、Dockerfile、SQL 模板 → 用
text/template - 不确定输出用途?先默认选
html/template,再按需禁用转义(见下一条)
{{.}} 和 {{.Field}} 的作用域与嵌套访问
. 始终代表当前作用域的顶层数据。传入的是结构体,. 就是那个结构体;传入的是 map,. 就是那个 map;传入的是字符串,. 就是那个字符串本身。
立即学习“go语言免费学习笔记(深入)”;
嵌套字段访问直接用点号链,无需额外语法:{{.User.Profile.AvatarURL}} 可以一路深入,前提是每层字段都导出(首字母大写)且非 nil。一旦中间某层为 nil,模板执行会 panic。
- 避免 panic:对可能为 nil 的字段,用
{{with .User.Profile}}{{.AvatarURL}}{{end}} - map 访问统一用
{{.MapKey}},不支持{{.Map["key"]}}语法 - 切片索引不支持直接写
{{.Items[0]}},得用{{index .Items 0}} - 想临时绑定一个字段到局部变量?
{{$name := .User.Name}},之后用{{$name}}
循环和条件里容易漏掉的空格与换行控制
Go 模板默认保留所有空白符,导致生成的 HTML 出现大量无意义缩进和空行。这不是 bug,是设计——但对前端调试和压缩不利。
用减号 - 修剪空白:{{- range .Items}} 删除前面所有空白(包括换行),{{end -}} 删除后面所有空白。注意:减号必须紧贴定界符,{{- range}} 合法,{{ - range}} 无效。
- 循环体内部每行开头/结尾的空格不会被自动删,需手动调整缩进或用
{{- }}显式修剪 -
{{if .Cond}}<p>yes</p>{{else}}<p>no</p>{{end}}如果前后有换行,会多出两行空行;加{{if .Cond -}}和{{- end}}可消除 - 注释
{{/* comment */}}不输出也不占空白,适合临时禁用某段逻辑
自定义函数注册后怎么安全调用
内置函数有限,比如没有 truncate、date_format、add。这时要用 template.Funcs() 注册,但要注意返回值约定:
函数签名必须是 func(interface{}) interface{} 或 func(interface{}) (interface{}, error)。后者若返回非 nil error,整个模板执行中断并报错。
- 注册示例:
t := template.New("t").Funcs(template.FuncMap{"add": func(a, b int) int { return a + b }}) - 模板中调用:
{{add .A .B}},参数顺序即函数定义顺序 - 管道调用更简洁:
{{.Price | printf "%.2f"}},printf是内置函数,支持格式化 - 禁止在自定义函数里做 IO、网络请求、锁操作——模板执行应是纯数据变换
真正难的不是语法,而是理解「上下文感知转义」的边界:它只保护你明确写进模板的动作,不保护你拼接的字符串、不保护你用 template.HTML 强制跳过转义的内容、也不保护你从外部注入的 JS/CSS 片段。这些地方,安全责任回到开发者手上。


















