自定义函数必须在加载模板前注册,r.setfuncmap()须置于r.loadhtmlglob()等加载方法之前,否则报“function not defined”;函数需首字母大写、签名严格匹配,推荐接收interface{}并类型断言以提升容错性。

自定义函数必须在加载模板前注册
很多开发者把 r.SetFuncMap() 放在 r.LoadHTMLGlob() 之后,结果模板里调用自定义函数时直接报错 function "xxx" not defined。这是因为 Gin 在加载模板时就完成了函数映射的绑定,后续再设无效。
-
r.SetFuncMap()必须在r.LoadHTMLGlob()、r.LoadHTMLFiles()或r.SetHTMLTemplate()之前调用 - 函数定义本身可以是包级变量或局部函数,但注册时必须是已声明、可导出的(首字母大写)
- 如果使用
template.ParseFS等自定义解析方式,需手动将FuncMap传入template.New().Funcs()
函数签名要匹配模板内调用方式
Go 模板函数不支持重载,参数类型和返回值必须严格匹配。比如你定义了 func FormatTime(t time.Time) string,那模板里就得写 {{FormatTime .CreatedAt}};若传入的是 int64 时间戳,就会 panic: can't convert int64 to time.Time。
- 常见错误:把
int当作time.Time传给格式化函数,或反过来 - 推荐统一接收
interface{}并内部类型断言,提高容错性(例如支持int64、time.Time、string多种输入) - 返回值建议始终为
string,避免模板中出现interface{}导致渲染失败
模板中调用自定义函数的语法限制
自定义函数不能像 Go 代码那样链式调用或带命名参数,只能是单层、位置参数调用。比如 {{UnixToTime .CreatedAt}} 合法,但 {{UnixToTime .CreatedAt "2006-01-02"}} 会报错——除非你定义的函数签名明确接收两个参数。
- 所有参数都按顺序传递,没有关键字参数支持
- 无法在模板里做条件判断后再调用(如
{{if .IsAdmin}}{{AdminOnlyFunc .Data}}{{else}}—{{end}}是合法的,但不能把if嵌进函数调用里) - 函数名区分大小写,且不能与内置函数同名(如覆盖
print会导致整个模板解析失败)
生产环境要注意 HTML 转义与 XSS 风险
html/template 默认会对函数返回值做 HTML 转义,这是安全机制。但如果你的自定义函数本意是输出已转义的 HTML(比如富文本渲染器),直接返回 string 会导致被二次转义,显示为源码而非渲染效果。
- 解决方法:返回
template.HTML类型,例如return template.HTML(htmlStr) - 但必须确保该字符串来自可信来源,否则绕过转义等于打开 XSS 入口
- 调试时可用
{{printf "%#v" (MyFunc .Data)}}查看实际返回类型,确认是否为template.HTML











