
Go的text/template和html/template通过fmt包(如fmt.Sprint或fmt.Fprint)生成值的默认文本表示,对实现了fmt.Stringer或error接口的类型可自定义输出,且需注意接收者类型匹配。
go的`text/template`和`html/template`通过`fmt`包(如`fmt.sprint`或`fmt.fprint`)生成值的默认文本表示,对实现了`fmt.stringer`或`error`接口的类型可自定义输出,且需注意接收者类型匹配。
在 Go 模板中,{{.}} 这类无显式函数调用的管道表达式,其输出依赖于 Go 标准库的格式化逻辑——并非模板引擎自身实现序列化,而是委托给 fmt 包。具体而言:
- text/template 在内部调用 fmt.Fprint(s.wr, value)(见 exec.go#L848 的 state.PrintValue);
- html/template 则使用 fmt.Sprint(args...)(见 content.go#L135 的 stringify),随后再进行上下文敏感的 HTML 转义。
这意味着:任何能被 fmt 包正确格式化的 Go 值,都会按其默认规则呈现。例如 map[string]interface{} 会输出形如 map[a:abc b:2] 的字符串——这恰好是 Go 语法合法的字面量,也常被用于前端 JS 数据注入(需确保安全)。
✅ 自定义默认表示:实现 fmt.Stringer
若希望控制输出,可让类型实现 fmt.Stringer 接口:
type MyMap map[string]interface{}
func (m MyMap) String() string {
return fmt.Sprintf("MyMap{%v}", map[string]interface{}(m))
}
t := template.Must(template.New("").Parse("{{.}}"))
mm := MyMap{"name": "Alice", "age": 30}
t.Execute(os.Stdout, mm) // 输出:MyMap{map[name:Alice age:30]}⚠️ 关键注意事项:
- 接收者必须与传入模板的值类型严格匹配:若 String() 使用值接收者(如上例),则需传入 MyMap 类型的值;若使用指针接收者 func (m *MyMap) String(),则必须传入 &mm(即 *MyMap 类型),否则 fmt 不会调用该方法。
- error 接口优先级高于 Stringer:若类型同时实现 Error() string 和 String() string,fmt 会优先调用 Error() —— 这在模板中同样生效。
? 安全提醒:html/template 的自动转义
在 html/template 中,即使 String() 返回 HTML 片段(如 <div>OK</div>),也会被自动转义为
func (m MyMap) String() string {
return `<strong>Custom</strong>` // → 被转义
}
// 正确方式(仅限 html/template):
func (m MyMap) HTML() template.HTML {
return template.HTML(`<strong>Custom</strong>`) // → 原样渲染
}? 总结
- 默认文本表示 = fmt 包的默认格式化行为(%v 规则);
- 可通过实现 fmt.Stringer(值/指针接收者需匹配)或 error 接口干预输出;
- html/template 会在 fmt 输出后追加上下文转义,不可绕过(除非显式使用 template.HTML 等安全类型);
- 调试时建议直接用 fmt.Printf("%v", v) 验证预期输出,结果与模板中 {{.}} 一致。

















