You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go使用embed.FS加载html/template异常 所有路由均返回test.html内容

问题原因

Go 标准库 html/template 的模板定义遵循全局共享、后加载覆盖前加载的规则:

  1. 你通过 template.ParseFS 一次性将所有视图模板、布局模板加载到同一个全局 view 模板实例中,home.html、test.html、process.html 三个业务模板都定义了名为 content 的全局模板块。
  2. {{define}} 指令的注册在模板解析阶段完成,和它在文件中的书写顺序无关,模板按文件遍历顺序加载,test.html 加载顺序最晚,它定义的 content 块直接覆盖了之前两个业务模板的同名块。
  3. 不管调用 ExecuteTemplate 执行哪个业务模板,模板内引用 base 布局时,base 调用的 content 块永远是最后被加载的 test 页面版本,因此所有路由都返回 test 页面的渲染结果。

额外说明:你代码中 init 函数的错误判断逻辑是冗余的,template.Must 方法已经内置了错误处理,解析失败会直接抛出 panic,后续判断 err != nil 的逻辑永远不会触发,可以直接删除。

解决方案

以下两种方案均基于标准库实现,任选其一即可:

方案1:拆分模板实例,业务模板单独解析(推荐)

不要全局共用一个包含所有业务模板的实例,预加载公共布局/组件模板,每次渲染对应路由时再单独解析对应业务模板,避免块名冲突:

// 全局只预加载公共布局和公共组件模板
var baseTpl *template.Template

func init() {
    baseTpl = template.Must(template.ParseFS(resources.Views, "templates/layouts/*.html", "templates/partials/*.html"))
}

修改路由处理函数,每个路由单独绑定对应业务模板:

func home(w http.ResponseWriter, r *http.Request) {
    // 克隆基础模板避免污染公共实例
    tpl, err := baseTpl.Clone()
    if err != nil {
        log.Fatalln(err)
    }
    // 仅解析当前路由对应的业务模板
    tpl = template.Must(tpl.ParseFS(resources.Views, "templates/views/home.html"))
    // 直接执行基础布局模板即可,会自动使用当前业务模板定义的content块
    err = tpl.ExecuteTemplate(w, "base", nil)
    if err != nil {
        log.Fatalln(err)
    }
}

// test、process 路由同理修改,仅替换解析的业务模板文件名即可
func test(w http.ResponseWriter, r *http.Request) {
    tpl, err := baseTpl.Clone()
    if err != nil {
        log.Fatalln(err)
    }
    tpl = template.Must(tpl.ParseFS(resources.Views, "templates/views/test.html"))
    err = tpl.ExecuteTemplate(w, "base", nil)
    if err != nil {
        log.Fatalln(err)
    }
}

func process(w http.ResponseWriter, r *http.Request) {
    tpl, err := baseTpl.Clone()
    if err != nil {
        log.Fatalln(err)
    }
    tpl = template.Must(tpl.ParseFS(resources.Views, "templates/views/process.html"))
    err = tpl.ExecuteTemplate(w, "base", nil)
    if err != nil {
        log.Fatalln(err)
    }
}

方案2:全局块重命名,避免重名冲突

如果你不想拆分模板实例,可以给每个业务模板的 content 块定义唯一名称,同时修改布局模板适配动态块名:

  1. 修改 base.html 布局,将固定调用 content 块改为接收块名参数:
{{define "base"}}
<!doctype html>
 <html lang="en">
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta charset="utf-8">
 </head>
 <body>
    {{template .ContentBlock .}}
</body>
</html>
{{end}}
  1. 修改所有业务模板的块名为唯一值,以home.html为例:
{{define "home.html"}}
{{template "base" map "ContentBlock" "home-content" "." .}}
{{end}}

{{define "home-content"}}
This is home
{{end}}

test.html和process.html同理修改块名为test-content、process-content即可。


内容的提问来源于stack exchange,提问作者Hasan A Yousef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 11:45:03