必须在路由注册前调用LoadHTMLGlob(),路径相对于工作目录而非源码目录,多级目录需用"**"通配符且每个模板首行须{{define "相对路径/文件名.html"}}严格匹配c.HTML()参数。

模板目录必须在 LoadHTMLGlob() 之前配置
Gin 不会自动扫描或延迟加载模板,router.LoadHTMLGlob() 必须在任何调用 c.HTML() 的路由注册之前执行,否则会 panic:「template: "xxx.html" is undefined」。常见错误是把 LoadHTMLGlob() 放在 router.GET() 后面,或者误以为启动后还能动态加载。
- 正确顺序:初始化 router → 调用
LoadHTMLGlob()或LoadHTMLFiles()→ 注册路由 →router.Run() - 路径必须相对于当前工作目录(不是
main.go所在目录),运行时建议统一用go run main.go在项目根目录执行 - 若用 IDE 运行,请检查其 working directory 设置是否为项目根目录,否则
"templates/**/*"会找不到文件
多级子目录必须用 ** 通配符且需 define 命名
当模板分散在 templates/web/、templates/admin/ 等多级目录时,仅靠目录结构无法识别——Gin 要求每个 HTML 文件开头显式声明 {{ define "web/index.html" }},否则即使文件存在、路径匹配,c.HTML(http.StatusOK, "web/index.html", ...) 仍会报错「template: "web/index.html" not found」。
- 通配符写法:
router.LoadHTMLGlob("templates/**/*")(两级)或"templates/**/**/*"(三级),不支持***或省略 - 每个模板文件第一行必须是
{{ define "相对路径/文件名.html" }},结尾必须有{{ end }} - 路径名必须和
c.HTML()第二个参数完全一致,大小写、斜杠方向、扩展名都不能错
单目录 vs 多目录:选 LoadHTMLGlob 还是 LoadHTMLFiles
LoadHTMLGlob() 更适合开发期快速加载,LoadHTMLFiles() 则适合上线后精确控制、避免意外加载隐藏文件(如 .DS_Store 或备份文件 index.html.bak)。
- 单目录(如全在
templates/):用router.LoadHTMLGlob("templates/*.html")最稳妥,避免匹配到非 HTML 文件 - 多目录但文件不多:直接列全路径更可控,例如
router.LoadHTMLFiles("templates/web/index.html", "templates/admin/dashboard.html") - 注意:
LoadHTMLFiles()不做路径展开,传入的字符串必须是真实存在的绝对或相对路径
常见路径错误与调试技巧
模板找不到时,Gin 默认只报错不提示实际搜索了哪些路径,容易卡在「为什么明明文件存在却说 not found」。
- 确认当前工作目录:在
main()开头加fmt.Println(os.Getwd()),看输出是否为你认为的项目根目录 - 检查文件权限:Windows 下有时因编辑器保存导致 BOM 头残留,可能干扰解析;Linux/macOS 注意文件是否可读
- 临时加日志:在
LoadHTMLGlob()后打印router.Delims或随便调用一次router.SetHTMLTemplate()并捕获 error,可提前暴露路径问题 - 最简复现法:先把模板挪到
templates/index.html,用LoadHTMLGlob("templates/*")和c.HTML(..., "index.html", ...)跑通,再逐步加目录和 define
Gin 模板路径看似简单,但实际踩坑点集中在「工作目录不可控」「define 名称与调用名不一致」「通配符层级写错」这三处,尤其是 define —— 它不是可选语法糖,而是多目录场景下的强制契约。


















