Gin模板渲染需显式加载文件或目录,否则c.HTML()会panic;必须在路由注册前调用LoadHTMLFiles/LoadHTMLGlob;数据须为gin.H或导出字段struct;HTML转义默认开启,需safe函数或template.HTML绕过;嵌套模板依赖{{define}}/{{template}};模板修改后需重启服务。

模板渲染在 Gin 中不是开箱即用的,必须显式加载文件或目录,否则 c.HTML() 会 panic 报错 “template not found”。
模板文件必须先用 LoadHTMLFiles 或 LoadHTMLGlob 加载
很多人写完 c.HTML() 就跑,结果 500 错误,日志里只有 html/template: "index.html" is undefined。这是因为 Gin 不会自动扫描目录——它压根不知道你有模板文件。
-
r.LoadHTMLFiles("templates/index.html", "templates/user.html"):适合模板少、路径明确的场景 -
r.LoadHTMLGlob("templates/**/*"):推荐用于多级目录(如templates/admin/dashboard.html),**表示递归匹配子目录 - 注意路径是运行时相对路径,不是源码路径;如果用
go run main.go,工作目录就是main.go所在目录 - 加载必须在任何路由注册前完成,放在
r := gin.Default()后、r.GET(...)前
c.HTML() 的第三个参数必须是 gin.H 或自定义 struct,不能传原始 map
Go 的 html/template 要求数据是导出字段(首字母大写)的 struct 或 gin.H(它是 map[string]interface{} 的别名,且 Gin 内部做了字段导出适配)。直接传 map[string]string{} 会导致模板里所有 .xxx 都为空。
- 正确写法:
c.HTML(200, "index.html", gin.H{"Title": "Home", "Items": []string{"a", "b"}}) - 错误写法:
c.HTML(200, "index.html", map[string]string{"title": "Home"})(title小写,模板无法访问) - 若用 struct,字段必须大写:
type PageData struct { Title string; Content string }
HTML 转义默认开启,要输出原始 HTML 必须用 template.HTML + 自定义函数
比如你想在模板里渲染一段带标签的富文本:{{ .Content }} 会变成转义后的 <p>Hello</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/gongju/2602" title="Gin框架 1.9.0"><img
src="https://img.php.cn/upload/manual/001/589/237/6a731f5b72949207.png" alt="Gin框架 1.9.0" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/gongju/2602" title="Gin框架 1.9.0">Gin框架 1.9.0</a>
<p>Gin框架 1.9.0版本源码包下载,版本号 1.9.0,适合需要 sonic JSON 支持、路由修复和内容协商改进的 Go Web 开发场景。</p>
</div>
<a href="/xiazai/gongju/2602" title="Gin框架 1.9.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>,浏览器显示为纯文本。这不是 bug,是 html/template 的安全机制。
- 解决方法一(推荐):在加载模板前注册安全函数:
r.SetFuncMap(template.FuncMap{"safe": func(s string) template.HTML { return template.HTML(s) }}),模板中写{{ .Content | safe }} - 解决方法二:用
template.HTML类型构造数据,例如gin.H{"Content": template.HTML("<p>Hello</p>")} - 注意:
SetFuncMap必须在LoadHTMLFiles/Glob之前调用,否则无效
嵌套模板需手动定义 base.html 并用 {{ template }} 引入子内容
Gin 本身不提供类似 Django 的 {% extends %} 语法,但底层用的是 Go 标准 html/template,所以支持 {{ define }} 和 {{ template }} 组合。
- 基础模板
templates/base.html中写:{{ define "base" }}...{{ template "content" . }}...{{ end }} - 子模板
templates/index.html中写:{{ define "content" }}<h1>{{ .Title }}</h1>{{ end }} - 渲染时仍用
c.HTML(200, "base.html", data),但必须确保两个文件都被LoadHTMLGlob加载到同一命名空间 - 常见坑:子模板没
{{ define "xxx" }},或 base 模板里{{ template "xxx" . }}名字拼错,都会静默失败(页面空白)
最易被忽略的一点:模板加载是一次性行为,修改模板文件后必须重启服务;热重载需要额外工具(如 air)或自己监听 fs 事件重新调用 LoadHTMLGlob —— Gin 默认不支持。

















