loadhtmlglob路径相对当前工作目录(os.getwd()),即执行go run或二进制时所在目录;必须在gin.default()后、路由注册前调用,且通配符需按go版本支持合理使用。

LoadHTMLGlob 路径到底是相对谁的?
不是相对 main.go,也不是相对项目根目录,而是相对**当前工作目录(os.Getwd())**——也就是你执行 go run main.go 或运行二进制时所在的目录。这意味着:在 GoLand 里右键“Run”,工作目录可能是项目根,也可能是模块子目录;用 cd cmd && go run ../main.go 就会直接 panic:html/template: pattern matches no files。
实操建议:
- 别写
router.LoadHTMLGlob("templates/*")这种裸路径,它极度依赖启动位置 - 改用
filepath.Abs(filepath.Join(filepath.Dir(os.Args[0]), "templates"))获取二进制所在目录下的templates绝对路径(适用于编译后部署) - 开发期更推荐
filepath.Abs(filepath.Join(".", "templates"))+ 显式检查os.Stat,失败时 panic 带出真实路径,方便定位
为什么用了 LoadHTMLGlob 却提示 template: "index" is undefined?
加载时机错了。router.LoadHTMLGlob 必须在 gin.Default() 之后、任何路由注册(如 router.GET)之前调用。否则 Gin 内部的 engine.HTMLRender.Templates 还没初始化,文件读了也白读,c.HTML 查不到模板名。
常见错误写法:
router := gin.Default()
router.GET("/", func(c *gin.Context) { c.HTML(200, "index.tmpl", nil) }) // ❌ 此时模板根本没加载
router.LoadHTMLGlob("templates/*") // ❌ 太晚了,已无用
正确顺序:
router := gin.Default()-
router.LoadHTMLGlob(...)(或LoadHTMLFiles(...)) -
router.GET(...)等路由定义
模板在子目录下怎么加载?通配符怎么写才不漏文件?
LoadHTMLGlob("templates/*") 只扫一级;LoadHTMLGlob("templates/**/*") 才递归匹配所有层级——但前提是你的 Go 版本 ≥ 1.16(filepath.Glob 支持 **),且模板文件名不含特殊字符。
如果遇到 ** 不生效或报错(比如某些旧环境),换用 filepath.WalkDir 手动收集:
var tplFiles []string
_ = filepath.WalkDir("templates", func(path string, d fs.DirEntry, _ error) error {
if !d.IsDir() && strings.HasSuffix(d.Name(), ".tmpl") {
tplFiles = append(tplFiles, path)
}
return nil
})
router.LoadHTMLFiles(tplFiles...)
注意:LoadHTMLFiles 接收的是完整路径(如 "templates/users/list.tmpl"),而 c.HTML 的第二个参数必须与文件名**完全一致**(含子目录路径和后缀),例如 c.HTML(200, "users/list.tmpl", ...)。
静态资源(CSS/JS)和模板路径是两回事
模板加载只影响 c.HTML 渲染;前端请求的 /css/app.css 是另一条 HTTP 路由,必须显式配置 router.Static("/css", "./templates/css")。两者路径互不感知——LoadHTMLGlob 不会自动暴露任何静态文件。
容易踩的坑:
- 误以为
templates/css/下的文件能被浏览器直接访问 → 必须配Static - 把
Static的本地路径写成"templates/css",但实际工作目录不是项目根 → 同样要用filepath.Abs构造绝对路径 -
Static的第一个参数(URL 前缀)不能以templates开头,否则语义混乱;推荐用"/static"或"/css"这类语义化前缀
最易忽略的一点:模板中引用静态资源的路径(如 <link href="/css/main.css">)是相对于网站根目录的,和 Go 代码里的文件系统路径完全无关——这层映射全靠 router.Static 配置来桥接。











