Iris 中需用 iris.RegisterView 配合手动构造的 *template.Template 注册自定义函数,函数名不能含点,safeHTML 应直接返回 template.HTML(s) 类型值,运行时上下文数据须通过 ViewData 传入,禁止在模板函数中执行 IO 或访问 Context。

iris.HTML 无法直接注册自定义函数?得换 RegisterView 方式
直接用 iris.HTML("./views", ".html") 返回的模板引擎不暴露 FuncMap 接口,你调 tmpl.AddFunc 会 panic。必须用 iris.RegisterView 配合手动构造的 template.Template 实例,才能安全注入函数。
核心逻辑是:先用 template.New 创建模板对象,再用 Funcs 注入函数映射,最后传给 iris.HTML 的构造器(它其实接受 template.Template 类型)。
- 不要写
app.RegisterView(iris.HTML(...))后再试图改函数 —— 晚了 - 必须在
iris.HTML初始化前,把带函数的*template.Template构造好 - 函数名不能含点(
.),否则模板解析时会误判为字段访问
怎么写一个安全的 HTML 转义绕过函数?别用 template.HTML
很多人想加 {{ .Content | safeHTML }},但直接返回 template.HTML 在 Iris 里可能被二次转义。正确做法是让函数返回 template.HTML 类型值,并确保模板引擎不额外处理它 —— 这要求函数本身不包装、不拼接字符串,只做类型转换。
示例函数:
func safeHTML(s string) template.HTML {
return template.HTML(s)
}
然后注册进 FuncMap:
funcs := template.FuncMap{
"safeHTML": safeHTML,
}
t := template.New("base").Funcs(funcs)
app.RegisterView(iris.HTMLFromTemplate(t, "./views", ".html"))
- 别写
return template.HTML(strings.ReplaceAll(s, "...", "..."))—— 容易引入 XSS - 别在函数里调
html.EscapeString再转template.HTML—— 白做,反而破坏原始意图 - Iris 默认开启自动转义,所以只有显式标记为
template.HTML的内容才跳过
模板函数访问 Context 或路由参数?不行,得靠 ViewData 中转
Go 标准库 html/template 的函数签名只能是 func(...interface{}) interface{},它拿不到 iris.Context,也没法读 ctx.Params() 或 ctx.Request().URL.Query()。想动态取当前路径、用户 ID、语言偏好?必须提前塞进 ctx.ViewData。
比如你要实现「当前路径高亮导航」,不能写 {{ isActive "user" }} 这种函数,而要:
app.Get("/user/{id}", func(ctx iris.Context) {
ctx.ViewData("currentPath", ctx.Path())
ctx.View("user.html")
})
然后在模板里用:
{{ if eq .currentPath "/user" }}active{{ end }}
- 试图在函数里通过闭包捕获
ctx会导致模板并发渲染出错(ctx不是线程安全的) - 所有需要运行时上下文的数据,必须由 handler 显式传入
ViewData - 如果多个页面都要用,可封装成中间件统一注入:
ctx.ViewData("now", time.Now())
函数里调用数据库或 HTTP 请求?别在模板层做
模板函数不是业务逻辑入口。你在 {{ getUserByID .id }} 里查数据库,等于把 IO 塞进渲染流程,会拖慢整个请求、阻塞 goroutine、且无法复用连接池和缓存。
正确顺序是:
- handler 中查好数据,比如
user, _ := repo.FindByID(id) - 通过
ctx.ViewData("user", user)传进去 - 模板里只做展示:
{{ .user.Name }},不触发任何副作用
如果你真需要「按需加载子模板」,应该用 {{ template "partial/user-card" .user }} + 预加载好所有依赖数据,而不是让函数现场拉。
最常被忽略的一点:Iris 的模板 reload 模式(tmpl.Reload(true))下,每次修改模板都会重建整个 *template.Template 实例 —— 所以函数注册逻辑必须放在 reload 触发前,否则新加的函数不会生效。


















