我一直在尝试理解 html/template 包与 golang 1.23 中的 embed 包的结合,但我什至很难获得一个可以运行两个页面的简单基本布局。如下详述,我遇到了一些内容覆盖(至少我怀疑这是问题),其中为不同的路线显示一个相同的 html 渲染。
我的最小示例的文件结构如下:
- templates/
- layout.html
- page1.html
- page2.html
- main.go
- go.mod
服务器嵌入 html 文件并使用标准 net/http 包(1.23)为其提供服务:
main.go
package main
import (
"embed"
"html/template"
"log"
"net/http"
)
//go:embed templates/*.html
var templateFS embed.FS
var templates *template.Template
func main() {
var err error
templates, err = template.ParseFS(templateFS, "templates/*.html")
if err != nil {
log.Fatal("Error parsing templates: ", err)
}
http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/page1", http.StatusSeeOther)
})
http.HandleFunc("GET /page1", func(w http.ResponseWriter, r *http.Request) {
err := templates.ExecuteTemplate(w, "page1", nil)
if err != nil {
http.Error(w, "Template rendering error: "+err.Error(), http.StatusInternalServerError)
}
})
http.HandleFunc("GET /page2", func(w http.ResponseWriter, r *http.Request) {
err := templates.ExecuteTemplate(w, "page2", nil)
if err != nil {
http.Error(w, "Template rendering error: "+err.Error(), http.StatusInternalServerError)
}
})
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
现在我尝试设计模板的方式是典型的基本布局,它通过子页面“填充”。
layout.html
{{ define "layout" }}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ block "title" . }}My Website{{ end }}</title>
</head>
<body>
<header>
<h1>Welcome!</h1>
<nav>
<a href="/page1">Page 1</a> |
<a href="/page2">Page 2</a>
</nav>
</header>
<main>
{{ block "content" . }}{{ end }}
</main>
</body>
</html>
{{ end }}
还有两个简单的子页面:
page1.html
{{ define "page1"}}
{{ template "layout" . }}
{{ end }}
{{ block "title" . }}Page 1{{ end }}
{{ block "content" . }}
<h2>This is Page 1</h2>
<p>Content for page 1 goes here.</p>
{{ end }}
page2.html
{{ define "page2"}}
{{ template "layout" . }}
{{ end }}
{{ block "title" . }}Page 2{{ end }}
{{ block "content" . }}
<h2>This is Page 2</h2>
<p>Content for page 2 goes here.</p>
{{ end }}
现在发生的情况是,对于
http://localhost:8080/page1
和 http://localhost:8080/page2
,即使我在两个路由的 ExecuteTemplate
函数调用中切换名称字符串,也会显示第 2 页的模板。
我已经对第 1/2 页的模板进行了很多实验(将块声明移入和移出定义,使用模板操作而不是块操作等)。我怀疑当涉及到 golang 的模板包或嵌入包引入的一些副作用时,我缺少一些非常重要的基础知识。你们能给我一点提示吗?
(我很乐意使用标准包来完成此操作,而不是 templp 等)
解析模板时,模板的顺序很重要。试试这个:
<!DOCTYPE HTML>
...
{{template "title" .}}
...
{{template "content" .}}}
...
{{define "title"}}{{end}}
{{define "content"}}{{end}}
page1:
{{define "title"}}Title1 contents{{end}}
{{define "content"}}Page 1 Contents{{end}}
page2:
{{define "title"}}Title2 contents{{end}}
{{define "content"}}Page 2 Contents{{end}}
page1Templ := template.Must(template.New("p1").Parse(layout))
template.Must(page1Templ.Parse(page1))
page2Templ := template.Must(template.New("p2").Parse(layout))
template.Must(page2Templ.Parse(page2))
这样每个模板都将包含布局和相应的页面定义。