英文:
Go: Templates Embedded in Binary Return Blank Page
问题
尝试将我的 Golang HTML 模板从文件移动到使用 embed
。
以下是代码示例:
//go:embed templates/*
var f embed.FS
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
// 从 layouts/ 和 includes/ 目录生成模板映射
layouts, err := embed.FS.ReadDir(f, "templates/layouts")
if err != nil {
panic(err.Error())
}
for _, layout := range layouts {
embeddedTemplate, err := template.ParseFS(f, "templates/layouts/"+layout.Name(), "templates/includes/base.tmpl")
if err != nil {
log.Println(err)
}
r.Add(layout.Name(), embeddedTemplate)
log.Println(layout.Name() + " loaded")
}
return r
}
在调试器中确认所有模板都不包含错误并且具有相应的内容。其他嵌入的文件(如静态资源)正常工作并正常提供服务。甚至从数据库加载的其他模板也正常工作。只有使用 embed
的模板最终为空白。
有什么提示可以解释这里发生了什么吗?谢谢!
编辑:完整示例:
main.go
package main
import (
"embed"
"html/template"
"log"
"path/filepath"
"github.com/gin-contrib/multitemplate"
"github.com/gin-gonic/gin"
)
//go:embed templates/*
var f embed.FS
func main() {
router := gin.Default()
router.HTMLRender = loadTemplates()
router.GET("/embed", HomeHandlerEmbed(router))
router.GET("/file", HomeHandlerFile(router))
router.Run(":8080")
}
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
// 从 embed FS 加载相同的模板
embeddedTemplate, err := template.ParseFS(f, "templates/layouts/home.tmpl", "templates/includes/base.tmpl")
if err != nil {
log.Println(err)
}
r.Add("homeEmbed.tmpl", embeddedTemplate)
log.Println("homeEmbed.tmpl" + " loaded from embed FS")
// 从真实文件系统加载相同的模板
layoutsFile, err := filepath.Glob("templates/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob("templates/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
for _, layout := range layoutsFile {
files := append(includes, layout)
r.Add(filepath.Base(layout), template.Must(template.ParseFiles(files...)))
log.Println(filepath.Base(layout) + ": " + files[0])
}
return r
}
func HomeHandlerEmbed(r *gin.Engine) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.HTML(200, "homeEmbed.tmpl", nil)
})
}
func HomeHandlerFile(r *gin.Engine) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.HTML(200, "home.tmpl", nil)
})
}
templates/includes/base.tmpl
<!DOCTYPE html>
<html>
<head>
{{template "head" .}}
</head>
<body>
{{template "body" .}}
</body>
</html>
templates/layouts/home.tmpl
{{define "head"}}<title>Test</title>{{end}}
{{define "body"}}
Body
{{end}}
/file
正常工作,但 /embed
显示为空白。
英文:
Trying to move my golang html templates from files to using embed
Works fine:
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
layouts, err := filepath.Glob("templates/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob("templates/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
// Generate our templates map from our layouts/ and includes/ directories
for _, layout := range layouts {
files := append(includes, layout)
r.Add(filepath.Base(layout), template.Must(template.ParseFiles(files...)))
log.Println(filepath.Base(layout) + ": " + files[0])
}
return r
}
Very similar code returns blank page, no errors:
//go:embed templates/*
var f embed.FS
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
// Generate our templates map from our layouts/ and includes/ directories
layouts, err := embed.FS.ReadDir(f, "templates/layouts")
if err != nil {
panic(err.Error())
}
for _, layout := range layouts {
embeddedTemplate, err := template.ParseFS(f, "templates/layouts/"+layout.Name(), "templates/includes/base.tmpl")
if err != nil {
log.Println(err)
}
r.Add(layout.Name(), embeddedTemplate)
log.Println(layout.Name() + " loaded")
}
return r
}
I confirmed in the debugger that all templates contain no errors and their respective content. Other embedded files such as static assets work fine and get served ok. Even other templates loaded from a database work fine. Just those from embed
end up blank.
Any hints what's happening here?
Thanks!
Edit: Full example:
main.go
package main
import (
"embed"
"html/template"
"log"
"path/filepath"
"github.com/gin-contrib/multitemplate"
"github.com/gin-gonic/gin"
)
//go:embed templates/*
var f embed.FS
func main() {
router := gin.Default()
router.HTMLRender = loadTemplates()
router.GET("/embed", HomeHandlerEmbed(router))
router.GET("/file", HomeHandlerFile(router))
router.Run(":8080")
}
func loadTemplates() multitemplate.Render {
r := multitemplate.New()
//load same template from embed FS
embeddedTemplate, err := template.ParseFS(f, "templates/layouts/home.tmpl", "templates/includes/base.tmpl")
if err != nil {
log.Println(err)
}
r.Add("homeEmbed.tmpl", embeddedTemplate)
log.Println("homeEmbed.tmpl" + " loaded from embed FS")
// load same template from real file system
layoutsFile, err := filepath.Glob("templates/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob("templates/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
for _, layout := range layoutsFile {
files := append(includes, layout)
r.Add(filepath.Base(layout), template.Must(template.ParseFiles(files...)))
log.Println(filepath.Base(layout) + ": " + files[0])
}
return r
}
func HomeHandlerEmbed(r *gin.Engine) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.HTML(200, "homeEmbed.tmpl", nil)
})
}
func HomeHandlerFile(r *gin.Engine) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.HTML(200, "home.tmpl", nil)
})
}
templates/includes/base.tmpl
<!DOCTYPE html>
<html>
<head>
{{template "head" .}}
</head>
<body>
{{template "body" .}}
</body>
</html>
templates/layouts/home.tmpl
{{define "head"}}<title>Test</title>{{end}}
{{define "body"}}
Body
{{end}}
/file works fine, /embed comes up blank
答案1
得分: 0
在loadTemplates()
函数中,只需修复这一行代码:
embeddedTemplate, err := template.ParseFS(f, "templates/includes/base.tmpl", "templates/layouts/home.tmpl")
在你的示例中,模板的顺序将按照以下顺序呈现:
- 第一个:
"templates/layouts/home.tmpl"
- 第二个:
"templates/includes/base.tmpl"
但是,如果我理解正确,对于函数template.ParseFS
来说,模板的顺序是重要的,因为base.tmpl
将包含在所有模板中。
函数template.ParseFS
会在进程中读取模板并尝试生成它们。
英文:
In function loadTemplates()
just fix this line:
embeddedTemplate, err := template.ParseFS(f, "templates/includes/base.tmpl", "templates/layouts/home.tmpl")
In your example patterns will be presented in this sequence:
- first:
"templates/layouts/home.tmpl"
- second:
"templates/includes/base.tmpl"
But if I understood correctly, the sequence of patterns is important for the function template.ParseFS
, because base.tmpl
will be included in all you templates.
The function template.ParseFS
reads the templates in the process and tries to generate them.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论