文章目录
目录
- 一、核心概念回顾
- 二、示例目标
- 三、项目结构(示意)
- 四、模板文件示例
- 五、Go 代码(完整、带模板缓存与 FuncMap)
- 六、要点与最佳实践
- 七、常见扩展需求与实现提示
- 八、总结
html/template:用于生成安全的 HTML,自动对变量做 HTML 转义,防止 XSS。
template.ParseFiles / template.ParseGlob:解析模板文件。
template.Execute / ExecuteTemplate:把数据渲染到模板并写入 http.ResponseWriter。
template.FuncMap:向模板注入自定义函数(格式化时间、生成 URL 等)。
- 模板缓存:避免在每次请求时重复解析模板,提高性能。
html/template:用于生成安全的 HTML,自动对变量做 HTML 转义,防止 XSS。template.ParseFiles / template.ParseGlob:解析模板文件。template.Execute / ExecuteTemplate:把数据渲染到模板并写入 http.ResponseWriter。template.FuncMap:向模板注入自定义函数(格式化时间、生成 URL 等)。
实现一个简单的博客首页(/)和文章详情页(/post/{id}):
- • 使用模板文件:
base.html,index.html,post.html - • 在模板中使用自定义函数
formatDate - • 采用模板缓存(预解析所有模板)
go-web-template/
├── main.go
└── templates/
├── base.html
├── index.html
└── post.html
templates/base.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>{{block "title" .}}My Blog{{end}}</title>
</head>
<body>
<header>
<h1>My Blog</h1>
<hr/>
</header>
<main>
{{block "content" .}}{{end}}
</main>
<footer>
<hr/>
<p>© 2025 My Blog</p>
</footer>
</body>
</html>
templates/index.html
{{define "title"}}首页 - My Blog{{end}}
{{define "content"}}
<h2>文章列表</h2>
<ul>
{{range .Posts}}
<li>
<a href="/post/{{.ID}}" rel="external nofollow" >{{.Title}}</a>
<small> - {{formatDate .CreatedAt}}</small>
</li>
{{else}}
<li>暂无文章</li>
{{end}}
</ul>
{{end}}
templates/post.html
{{define "title"}}{{.Post.Title}} - My Blog{{end}}
{{define "content"}}
<article>
<h2>{{.Post.Title}}</h2>
<p><em>发布时间:{{formatDate .Post.CreatedAt}}</em></p>
<div>
{{.Post.Content}} <!-- html/template 会自动转义,若内容含 HTML 需谨慎处理 -->
</div>
</article>
<p><a href="/" rel="external nofollow" >返回</a></p>
{{end}}
package main
import (
"html/template"
"log"
"net/http"
"path/filepath"
"sync"
"time"
"fmt"
)
// 简单的文章结构体
type Post struct {
ID int
Title string
Content string
CreatedAt time.Time
}
// 全局模板缓存
var (
templates *template.Template
once sync.Once
)
// 自定义模板函数:格式化日期
func formatDate(t time.Time) string {
return t.Format("2006-01-02 15:04")
}
// 预解析并缓存模板
func loadTemplates(dir string) {
funcs := template.FuncMap{
"formatDate": formatDate,
}
pattern := filepath.Join(dir, "*.html")
tmpl, err := template.New("").Funcs(funcs).ParseGlob(pattern)
if err != nil {
log.Fatalf("解析模板失败: %v", err)
}
templates = tmpl
}
// 渲染模板的辅助函数
func render(w http.ResponseWriter, name string, data any) {
once.Do(func() { loadTemplates("templates") }) // 只加载一次
w.Header().Set("Content-Type", "text/html; charset=utf-8")
err := templates.ExecuteTemplate(w, name+".html", data)
if err != nil {
http.Error(w, "模板渲染错误: "+err.Error(), http.StatusInternalServerError)
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
// 假数据
posts := []Post{
{ID: 1, Title: "第一篇文章", Content: "这是第一篇文章的内容。", CreatedAt: time.Now().Add(-48 * time.Hour)},
{ID: 2, Title: "第二篇文章", Content: "这是第二篇文章的内容。", CreatedAt: time.Now().Add(-24 * time.Hour)},
}
render(w, "index", map[string]any{
"Posts": posts,
})
}
func postHandler(w http.ResponseWriter, r *http.Request) {
// 简单路由:/post/1
var id int
_, err := fmt.Sscanf(r.URL.Path, "/post/%d", &id)
if err != nil {
http.NotFound(w, r)
return
}
// 假数据(实际应从 DB 查询)
post := Post{
ID: id,
Title: fmt.Sprintf("文章 #%d", id),
Content: "这里是文章正文。注意:如果包含 HTML 内容,需要经过白名单清洗或标记为安全 HTML。",
CreatedAt: time.Now().Add(-time.Duration(id) * time.Hour),
}
render(w, "post", map[string]any{
"Post": post,
})
}
func main() {
http.HandleFunc("/", indexHandler)
http.HandleFunc("/post/", postHandler)
log.Println("服务启动:http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("服务器启动失败: %v", err)
}
}
- 1. 使用
html/template 而不是 text/template 以防 XSS:前者专为 HTML 安全设计,会对插入的字符串进行自动转义。
- 2. 模板缓存:在生产环境不要在每次请求中解析模板文件(成本高);应在启动时或首次请求时预解析并缓存模板(示例使用
sync.Once)。
- 3. 模板复用/继承:通过
{{block}}/{{define}} 模式实现基模板(base.html)与页面片段的复用。
- 4.
template.FuncMap 注入工具函数:把常用格式化/帮助函数注入模板(例如:formatDate, safeHTML 等)。
- 5. 小心 HTML 内容的输出:如果你需要在模板中渲染可信任的 HTML(例如 CMS 的富文本内容),应使用
template.HTML 明确标记信任,但这是危险操作,必须经过严格过滤与白名单处理。
- 6. 并发安全:
template.Template 的 Execute 是并发安全的(可多协程并发调用已解析的模板),只要模板在并发前完成解析并不再修改即可。
- 7. 国际化(i18n) :可通过 FuncMap 注入
T 函数来实现文本翻译。
- 8. 开发时热加载:在开发环境可以跳过缓存、在每次请求重新解析模板以便即时查看修改效果(便于调试)。
html/template 而不是 text/template 以防 XSS:前者专为 HTML 安全设计,会对插入的字符串进行自动转义。sync.Once)。{{block}}/{{define}} 模式实现基模板(base.html)与页面片段的复用。template.FuncMap 注入工具函数:把常用格式化/帮助函数注入模板(例如:formatDate, safeHTML 等)。template.HTML 明确标记信任,但这是危险操作,必须经过严格过滤与白名单处理。template.Template 的 Execute 是并发安全的(可多协程并发调用已解析的模板),只要模板在并发前完成解析并不再修改即可。T 函数来实现文本翻译。
- 模板局部缓存与静态资源 URL 版本控制:在模板函数中生成带 hash 的静态资源 URL,方便前端缓存失效控制。
- 模板层错误处理:在模板中谨慎处理可能为
nil 的值,避免渲染出错。
- 组件化模板:把常用组件(导航、分页、卡片)拆成单独模板文件并
template.ParseFiles 引入。
- 模板安全策略:对用户输入的富文本,使用 HTML 清洗库(如 bluemonday)过滤后再标记为
template.HTML。
- 将模板渲染与 JSON API 共存:同一服务既返回 HTML 页面也提供 JSON API,依据请求头
Accept 或 URL 路径区分。
nil 的值,避免渲染出错。template.ParseFiles 引入。template.HTML。Accept 或 URL 路径区分。
使用 Go 的 html/template 可以非常方便且安全地实现服务端 HTML 渲染:
- 简单:模板语法直观易学;
- 安全:默认转义机制防止 XSS;
- 高效:模板可预解析并复用,支持并发执行。
以上就是Go语言使用模板渲染HTML页面的实现技巧的详细内容,更多关于Go模板渲染HTML页面的资料请关注风君子博客其它相关文章!
您可能感兴趣的文章:
- Go语言Gin框架实现HTML页面渲染
- Go Web下gin框架的模板渲染的实现
- Golang使用gin模板渲染base64图片出现#ZgotmplZ的解决办法
- Go模板后端渲染时vue单页面冲突