必须先注册自定义truncate函数,再在模板中调用;注册需在LoadHTMLGlob之后、Run之前,通过r.SetFuncMap()传入gin.FuncMap,函数签名为func(string, int) string,原文长度≤指定长度时原样返回。

模板中用 {{ . | truncate 50 }} 实现基础截断
Gin 默认不提供 truncate 模板函数,直接写 {{ . | truncate 50 }} 会报错 template: ...: function "truncate" not defined。必须先注册自定义函数,再在模板里调用。Gin 的 html/template 引擎只认你显式注册的函数,不会自动带任何文本处理工具。
注册 truncate 函数要传入 *template.Template
注册时机必须在 r.LoadHTMLGlob(...) 或 r.LoadHTMLFiles(...) 之后、r.Run() 之前;且要通过 r.SetFuncMap() 把函数挂到引擎上,不能只改全局 template.FuncMap —— Gin 不读那个。
-
r.SetFuncMap()接收的是gin.FuncMap类型(本质是map[string]interface{}),键是模板里用的名字,值是函数本身 - 函数签名推荐用
func(string, int) string,接收原文和最大长度,返回截断后字符串(含省略号) - 注意:如果原文长度 ≤ 指定长度,应原样返回,避免多加省略号
示例注册逻辑:
r := gin.Default()
r.LoadHTMLGlob("templates/*")
r.SetFuncMap(gin.FuncMap{
"truncate": func(s string, n int) string {
if len(s) <= n {
return s
}
if n <= 3 {
return s[:n]
}
return s[:n-3] + "..."
},
})
模板里调用时注意参数顺序和类型
{{ .Content | truncate 100 }} 是正确写法;{{ truncate .Content 100 }} 会报错 —— Go 模板只支持管道语法调用函数,不支持括号调用。另外,传入值必须是 string 类型,如果字段是 interface{} 或结构体字段,需提前确保可转为字符串,否则运行时报 can't apply field selector 或空字符串。
- 常见踩坑:前端传来的 JSON 字段如
"desc": null,后端用gin.H{"desc": nil}传入模板,{{ .desc | truncate 50 }}会静默输出空串,不报错也不提示 - 安全做法:在 handler 中预处理,比如
desc := "",再赋值;或函数内部加if s == nil { return "" }判断(此时函数签名需改为func(interface{}, int) string)
中文截断要按 rune 数而非字节长度
上面示例用 len(s) 是按字节算的,对中文会出错:一个汉字占 3 字节,len("你好") == 6,设 n=4 就可能切在 UTF-8 中间,渲染出 符号。真正该用 utf8.RuneCountInString(s)。
修正后的函数:
import "unicode/utf8"
// ...
"truncate": func(s string, n int) string {
r := []rune(s)
if len(r) <= n {
return s
}
if n <= 3 {
return string(r[:n])
}
return string(r[:n-3]) + "..."
},
这是最容易被忽略的一点:开发时用英文测试没问题,一上生产遇到中文就乱码,但错误不抛在 Gin 日志里,只体现在页面上,排查成本高。


















