Go html/template不同目录同名模板如何按路由方法指定调用
问题:不同目录下存在同名的Go html/template模板时,如何为不同路由的处理方法指定调用对应的模板?
问题根因
Go标准库html/template默认使用文件本身的名称作为模板的唯一标识,和文件所在目录无关。你先后加载user/index.html和agency/index.html时,两个模板的名称都是index.html,后加载的模板会直接覆盖先加载的同名模板,所以渲染用户列表时实际调用的是agency的模板,才会出现字段不匹配的报错。
最优解决方案:自定义模板命名规则,保留相对路径
你想要的"templates/user/index.html"这种调用方式完全可以实现,只需要自己遍历模板目录,将文件的相对路径作为模板名注册即可,不需要修改原有目录结构和文件名:
第一步:实现自定义模板加载函数
替换原有全局ParseGlob逻辑,改用路径遍历的方式加载所有模板,用文件相对于项目根的路径作为模板名:
import ( "html/template" "os" "path/filepath" "strings" ) func loadTemplates(rootDir string) (*template.Template, error) { // 初始化空模板集合 tpl := template.New("") // 遍历模板根目录下所有文件 err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // 跳过目录,只处理后缀为.html的文件 if info.IsDir() || !strings.HasSuffix(info.Name(), ".html") { return nil } // 转换为斜杠分隔的相对路径,保证Windows、Linux/macOS路径格式统一 relPath, err := filepath.Rel(".", path) if err != nil { return err } relPath = filepath.ToSlash(relPath) // 读取模板文件内容 content, err := os.ReadFile(path) if err != nil { return err } // 以相对路径为模板名,注册到模板集合中 _, err = tpl.New(relPath).Parse(string(content)) return err }) if err != nil { return nil, err } return tpl, nil }
第二步:main函数中替换模板加载逻辑
func main() { // 加载所有模板,自动保留相对路径作为模板名 tpl := template.Must(loadTemplates("templates")) handlers := &handler.Handler{ DB: db, Tmpl: tpl, } // 后续路由注册逻辑保持不变 r := mux.NewRouter() r.HandleFunc("/", handlers.Index).Methods("GET") r.HandleFunc("/user", handlers.UserList).Methods("GET") r.HandleFunc("/agency", handlers.AgencyList).Methods("GET") // 省略ListenAndServe等逻辑 }
第三步:handler中按路径调用对应模板
直接用你期望的写法调用即可:
// 用户列表调用user目录下的index.html func (h *Handler) UserList(w http.ResponseWriter, r *http.Request) { Users := []*User{} // 省略数据查询逻辑 err = h.Tmpl.ExecuteTemplate(w, "templates/user/index.html", struct{ Users []*User }{Users: Users}) // 省略错误处理 } // 机构列表调用agency目录下的index.html func (h *Handler) AgencyList(w http.ResponseWriter, r *http.Request) { Agencys := []*Agency{} // 省略数据查询逻辑 err = h.Tmpl.ExecuteTemplate(w, "templates/agency/index.html", struct{ Agencys []*Agency }{Agencys: Agencys}) // 省略错误处理 }
可选方案:按业务拆分独立模板实例
如果业务模块较少,也可以给每个业务单独初始化模板实例,从根源上避免重名问题:
type Handler struct { DB *sql.DB UserTmpl *template.Template AgencyTmpl *template.Template } // main中加载逻辑 userTpl := template.Must(template.ParseGlob("templates/user/*.html")) agencyTpl := template.Must(template.ParseGlob("templates/agency/*.html")) handlers := &handler.Handler{ DB: db, UserTmpl: userTpl, AgencyTmpl: agencyTpl, }
调用时直接使用对应业务的模板实例即可:h.UserTmpl.ExecuteTemplate(w, "index.html", data)。
内容的提问来源于stack exchange,提问作者Johann Schatten
相关产品推荐
相关产品推荐

