Go 模板初识

🥰个人主页:会编程的土豆(欢迎来访)

💎作者简介:后端学习者

❄️个人专栏:数据结构与算法数据库leetcode

那些你一个人走过的夜路,终将化作照亮未 来的光

目标:搞清楚模板是什么、标准库怎么解析与执行、text/templatehtml/template 的区别,并能写出第一个可运行的渲染示例。


1. 为什么需要模板?

做 Web 或生成报告时,经常要把动态数据填进一段相对固定的文本里。

如果不使用模板,很容易写成这样:

复制代码
html := "<h1>Hello, " + user.Name + "</h1>"
html += "<p>年龄:" + strconv.Itoa(user.Age) + "</p>"

问题很明显:

  1. 难维护:HTML 结构和 Go 代码缠在一起
  2. 易出错:引号、转义、换行一多就乱
  3. 不安全:用户输入直接拼进 HTML,可能造成 XSS
  4. 难复用:页头页脚无法干净地拆分复用

模板引擎的思路是:视图(模板文件)和数据(Go 里的 struct/map)分离

复制代码
模板文件(长什么样) + 数据(填什么) → 最终输出(HTML/文本)

Go 标准库提供了两套模板引擎,语法几乎相同:

用途
text/template 纯文本:邮件正文、配置、代码生成等
html/template HTML:自动按上下文转义,降低 XSS 风险

做网页时,优先用 html/template


2. 模板长什么样?

最简单的 HTML 模板:

复制代码
<!-- hello.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>问候</title>
</head>
<body>
  <h1>Hello, {{.Name}}!</h1>
  <p>你今年 {{.Age}} 岁。</p>
</body>
</html>

关键点是双花括号 {``{ }}

  • {``{.Name}}:输出当前数据的 Name 字段
  • 这里的 .当前上下文(dot) ,一开始等于你传入 Execute 的那份数据

模板里普通文字原样输出;只有 {``{ }} 里的内容由引擎计算


3. 三个核心步骤:Parse → 准备数据 → Execute

用一句话概括标准库用法:

  1. Parse:把模板文本解析成内存中的模板对象
  2. 准备数据:构造 struct / map / 基本类型
  3. Execute:把数据灌进模板,写出结果

3.1 完整最小示例(输出到终端)

复制代码
package main

import (
	"html/template"
	"os"
)

type User struct {
	Name string
	Age  int
}

func main() {
	const tpl = `Hello, {{.Name}}! Age={{.Age}}`
	t, err := template.New("hello").Parse(tpl)
	if err != nil {
		panic(err)
	}

	u := User{Name: "七米", Age: 18}
	if err := t.Execute(os.Stdout, u); err != nil {
		panic(err)
	}
}

运行结果:

复制代码
Hello, 七米! Age=18

逐步说明:

  • template.New("hello"):创建一个名为 hello 的模板
  • .Parse(tpl):解析字符串模板
  • Execute(os.Stdout, u):以 u 为根数据 .,结果写到标准输出

3.2 从文件解析(更贴近真实项目)

复制代码
package main

import (
	"html/template"
	"net/http"
)

type User struct {
	Name string
	Age  int
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		tmpl, err := template.ParseFiles("hello.html")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		data := User{Name: "七米", Age: 18}
		if err := tmpl.Execute(w, data); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	})
	http.ListenAndServe(":8080", nil)
}

浏览器访问 http://localhost:8080,就会看到渲染后的页面

说明:

  • ParseFiles:从磁盘读模板
  • Execute(w, data)whttp.ResponseWriter,实现了 io.Writer,所以可以直接当输出目标
  • 生产环境通常不会每次请求都 ParseFiles(后面会讲缓存/Must/一次加载),入门先求正确

4. template.Must:启动期失败就直接崩

解析模板失败时,很多程序选择「进程直接退出」,避免带着坏模板跑服务:

复制代码
tmpl := template.Must(template.ParseFiles("hello.html"))

Must 的逻辑等价于:

复制代码
t, err := template.ParseFiles("hello.html")
if err != nil {
	panic(err)
}

适合在 main 或包初始化时加载模板;不适合在每个请求里乱用(panic 会打挂 goroutine/进程)。


5. 数据可以是什么?

模板的根数据常见有三类。

5.1 结构体(最常用)

复制代码
type PageData struct {
	Title string
	User  User
}

模板:

复制代码
<title>{{.Title}}</title>
<p>{{.User.Name}}</p>

字段必须导出(首字母大写),否则模板访问不到。

5.2 map

复制代码
data := map[string]any{
	"Title": "首页",
	"Name":  "七米",
}

模板:

复制代码
<title>{{.Title}}</title>
<p>{{.Name}}</p>

Gin 里的 gin.H 本质上就是 map[string]any,后面 lesson09 会用到。

5.3 基本类型

复制代码
tmpl.Execute(w, "世界")

模板:

复制代码
Hello, {{.}}!

此时 . 本身就是字符串 "世界"


6. text/template vs html/template

语法几乎一样,差别在输出是否做 HTML 安全处理

对比实验

假设用户名是恶意输入:

复制代码
name := `<script>alert("xss")</script>`
text/template

会原样输出脚本标签,浏览器可能执行 JS(危险)。

html/template

会转义成类似:

复制代码
&lt;script&gt;alert(&#34;xss&#34;)&lt;/script&gt;

页面上显示的是文本,而不是可执行脚本。

怎么选?

场景 选择
返回 HTML 页面 html/template
生成纯文本邮件、SQL 片段以外的文本、代码 text/template
不确定 Web 相关一律 html

七米课里讲 Web,后续基本围绕 html/template


7. Parse 家族 API 速览

入门阶段先熟悉这几个:

复制代码
// 1) 字符串
template.New("name").Parse(`Hello {{.Name}}`)

// 2) 一个或多个文件
template.ParseFiles("a.html", "b.html")

// 3) 按 glob 匹配
template.ParseGlob("templates/*.html")

注意:

  • ParseFiles / ParseGlob 返回的是一个模板集合*template.Template
  • 集合里可以有多个命名模板
  • 单文件时,Execute 通常就能工作;多文件时更推荐 ExecuteTemplate(lesson06 会细讲)

ExecuteExecuteTemplate

复制代码
tmpl.Execute(w, data)                 // 执行「默认/主」模板
tmpl.ExecuteTemplate(w, "home", data) // 按名字执行集合中的某个模板

初学可以先记住:

  • 只有一个简单文件:Execute 够用
  • 一旦出现 {``{define "xxx"}} 或多个页面:用 ExecuteTemplate

8. 一个稍完整的入门例子

目录:

复制代码
lesson04-demo/
  main.go
  hello.html

hello.html

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>{{.Title}}</title>
  <style>
    body { font-family: sans-serif; max-width: 640px; margin: 40px auto; }
    .card { border: 1px solid #ddd; padding: 16px; border-radius: 8px; }
  </style>
</head>
<body>
  <h1>{{.Title}}</h1>
  <div class="card">
    <p>用户:{{.User.Name}}</p>
    <p>年龄:{{.User.Age}}</p>
  </div>
</body>
</html>

main.go

复制代码
package main

import (
	"html/template"
	"log"
	"net/http"
)

type User struct {
	Name string
	Age  int
}

type ViewData struct {
	Title string
	User  User
}

func main() {
	tmpl := template.Must(template.ParseFiles("hello.html"))

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		data := ViewData{
			Title: "模板初识 Demo",
			User:  User{Name: "七米", Age: 18},
		}
		if err := tmpl.Execute(w, data); err != nil {
			log.Println("execute:", err)
		}
	})

	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

运行:

复制代码
go run .

打开浏览器看效果。把 User.Name 改成带 <script> 的字符串,观察 html/template 的转义行为------这是初识阶段很值得做的小实验。


9. 常见新手坑(lesson04 就会碰到)

坑 1:字段不大写

复制代码
type User struct {
	name string // 错:模板访问不到
}

模板 {``{.name}} 会失败或为空(严格模式下报错)。应写成 Name

坑 2:文件路径不对

ParseFiles("hello.html") 相对的是进程工作目录 ,不是源码文件所在目录。

在别的目录执行 go run 时容易找不到文件。

坑 3:忽略 error

Parse / Execute 都可能失败。至少要打日志或返回 500,否则页面空白却不知道原因。

坑 4:用 text/template 渲 HTML

能跑,但不安全。Web 场景请用 html/template

坑 5:每次请求都 Parse(性能)

入门可以接受;真正服务应在启动时解析一次并复用 *template.Template(它是并发安全可复用的)。

模板描述形状,数据提供内容;用 html/template 解析后 Execute 出去。

相关推荐
腾讯蓝鲸智云1 小时前
标杆客户实践提炼:CFlow 版本价值流模板实战指南
运维·服务器·自动化·云计算·devops
阿标的博客2 小时前
Active Directory 组织单位和用户
服务器
Deryck_德瑞克2 小时前
【Nginx】配置差异分析
服务器·前端·nginx
前端 贾公子2 小时前
第06章:结构化输出 (上)
java·服务器·前端
Yang96113 小时前
自动化射频测试台快速搭建,鼎讯信通 DXSL 矢量信号源模块二次开发集成指南
运维·自动化
一只小菜鸡..3 小时前
南京大学 操作系统 (JYY) 学习笔记:持久化的黑魔法——从磁铁、刻坑到闪存电荷
服务器·笔记·学习
翼龙云_cloud3 小时前
腾讯云国际站代理商: COS 对象存储 + CDN加速 从创建到加速完成
运维·服务器·云计算·腾讯云
aFakeProgramer3 小时前
Linux版本兼容问题及RTA-VRTE环境配置笔记
linux·笔记
Zhu7583 小时前
在Ubuntu24快速配置专用SFTP
linux·sftp