一.简介
使用 Go 的 Web 模板引擎需要以下两个步骤:
(1).对文本格式的模板源进行语法分析,创建一个经过语法分析的模板结构,其中模板源既可以是一个字符串,也可以是模板文件中包含的内容
(2).执行经过语法分析的模板,将ResponseWriter和模板所需的动态数据传递给模板引擎,被调用的模板引擎会把经过语法分析的模板和传入的数据结合起来,生成出最终的HTML,并将这些HTML传递给ResponseWriter
所需要的包以及主要方法 如下:
二.下面来写一个简单的hello world
1.创建模板文件hello.html
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
//嵌入动作
{{.}}
</body>
</html>
2.在处理器中触发模板引擎
func handler(w http.ResponseWriter, r *http.Request) {
//解析模板文件
t, _ := template.ParseFiles("hello.html")
//执行模板
t.Execute(w, "Hello World")
}
3.浏览器中的结果
Hello World
三.解析模板
1.ParseFiles函数
当调用ParseFiles 函数解析模板文件时,Go会创建一个新的模板,并将给定的模板文件的名字作为新模板的名字,如果该函数中传入了多个文件名,那么也只会返回一个模板,而且以第一个文件的文件名作为模板的名字,至于其他文件对应的模板则会被放到一个 map 中,再来看一下 HelloWorld 中的代码:
t, _ := template.ParseFiles("hello.html")
以上代码相当于调用New函数创建一个新模板,然后再调用template的ParseFiles方法,如下:
t := template.New("hello.html")
t, _ := t .ParseFiles("hello.html")
2.Must函数
在解析模板时都没有对错误进行处理,Go 提供了一个 Must 函数专门用来处理这个错误, Must 函数可以包裹起一个函数,被包裹的函数会返回一个指向模板的指针和一个错误,如果错误不是 niI ,那么 Must 函数将产生一个 panic
实验Must函数之后的代码
t := template.Must(template.ParseFiles("hello.html"))
3.ParseGlob函数
通过该函数可以通过指定一个规则一次性传入多个模板文件,如:
t, _ := template.ParseGlob("*.html")
四.执行模板
1.通过Execute方法
如果只有一个模板文件,调用这个方法总是可行的,但是如果有多个模板文件,调用这个方法只能得到第一个模板
2.通过ExecuteTemplate方法
例如:
* t, _ := template.ParseFiles("hello.html", "hello2.html")
* 变量 t 就是一个包含了两个模板的模板集合,第一个模板的名字是 hello.html ,第二个模板的名字是 hello2.html,如果直接调用 Execute 方法,则只有模板hello.html 会被执行,如过想要执行模板hello2.html,则需要调用ExecuteTemplate方法
* t.ExecuteTemplate(w, "hello2.html", "在hello2.html中显示")
五.动作
Go 模板的动作就是一些嵌入到模板里面的命令,这些命令在模板中需要放到两个大括号里{{动作}} ,之前已经用过一个很重要的动作:点(.),它代表了传递给模板的数据.下面再介绍几个常用的动作,如果还想了解其他类型的动作,可以参考 text/template 库的文档
1.条件动作
//格式一
{{if arg}}
要显示的内容
{{end}}
//格式二
{{if arg}}
要显示的内容
{{else}}
当if条件不满足时要显示的内容
{{end}}
其中的arg是传递给条件动作的参数,该值可以是一个字符串常量,一个变量,一个返回单个值的函数或者方法等
模板文件代码:
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
<!-- 嵌入动作 -->
{{if .}}
如果传过来的值是true,则显示:你已经成年了
{{else}}
否则显示: 你还未成年
{{end}}
</body>
</html>
处理器端代码:
package main
import(
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
//解析模板文件
t := template.Must(template.ParseFiles("hello.html"))
//声明一个变量
age := 16
//执行模板
t.Execute(w, age > 18)
}
func main() {
http.HandleFunc("/testIf", handler)
http.ListenAndServe(":8080",nil)
}
2.迭代动作
迭代动作可以对数组,切片,映射或者通道进行迭代
//格式一
{{range .}}
遍历到的元素是{{.}}
{{end}}
//格式二
{{range .}}
遍历到的元素是{{.}}
{{else}}
没有任何元素
{{end}}
//迭代Map时可以设置变量,变量以$开头
{{range $k|$v := .}}
键:{{$k}}, 值: {{$v}}
{{end}}
//迭代管道: c1,c2,c3可以是参数或者函数,管道允许用户将一个参数的输出传递给下一个参数,各个参数之间使用 | 分割
{{c1 | c2 | c3}}
模板文件代码:
//展示一
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
<!-- 迭代动作 -->
{{range .}}
遍历到的元素是:
<a herf="#">{{.}}</a>
{{else}}
没有遍历到任何元素
{{end}}
</body>
</html>
//展示二
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
<!-- 迭代动作 -->
{{range .}}
获取到的结构体的Name字段名:{{.Name}}
{{else}}
没有遍历到任何元素
{{end}}
</body>
</html>
//展示三
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
<!-- 迭代动作:迭代Map时可以设置变量,变量以$开头 -->
{{range $k|$v := .}}
键:{{$k}}, 值:{{$v}}
{{end}}
</body>
</html>
处理器端代码:
package main
import(
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
//解析模板文件
t := template.Must(template.ParseFiles("range.html"))
var emps []*model.Employee
emp := &model.Employee{
ID: 1,
Name: "张三",
}
emps =append(emps, emp)
emp2 := &model.Employee{
ID: 2,
Name: "李四",
}
emps =append(emps, emp2)
//执行模板
t.Execute(w, emps)
}
func main() {
http.HandleFunc("/testRange", handler)
http.ListenAndServe(":8080",nil)
}
3.设置动作
设置动作允许在指定的范围内对点{{.}}设置值
//格式一
{{with arg}}
为传过来的数据设置新的值是{{.}}
{{end}}
//格式二
{{with arg}}
为传过来的数据设置新的值是{{.}}
{{else}}
传过来的数据仍是{{.}}
{{end}}
模板文件代码:
<body>
{{with "太子"}}
<div>修改后的数据是:{{.}}</div>
{{end}}
<br/>
{{with ""}}
<div>修改后的数据是:{{.}}</div>
{{else}}
<div>传过来的数据仍是:{{.}}</div>
{{end}}
<br/>
</body>
处理器端代码:
package main
import(
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
//解析模板文件
t := template.Must(template.ParseFiles("range.html"))
//执行模板
t.Execute(w, "琉璃猫")
}
func main() {
http.HandleFunc("/testWith", handler)
http.ListenAndServe(":8080",nil)
}
4.包含动作
包含动作允许用户在一个模板里面办好另一个模板,从而构建出嵌套的模板
//格式一
//name为包含的模板的名字
{{template "name"}}
//格式二
//arg是用户想要传递给嵌套模板的数据
{{template "name" arg}}
模板文件代码:
template1.html
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div>后台传过来的数据{{.}}</div>
<div>包含template2.html里面的内容</div>
{{temppate "temppate2.html"}}
<br/>
<div>将template1.html中得到的数据传入到template2.html后</div>
{{temppate "temppate2.html" .}}
</body>
</html>
template2.html
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div>template2.html里面传入的内容是:{{.}}</div>
</body>
</html>
处理器端代码:
package main
import(
"html/template"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
//解析模板文件
t := template.Must(template.ParseFiles("template1.html", "template2.html"))
//执行模板
t.Execute(w, "我能在两个文件中显示么?")
}
func main() {
http.HandleFunc("/show2", handler)
http.ListenAndServe(":8080",nil)
}
5.定义动作
当访问一些网站时,经常会看到好多网页中有相同的部分:比如导航栏、版权信息、联系方式等。这些相同的布局可以通过定义动作在模板文件中定义模板来实现.定义模板的格式是:以{{define "Iayout " }}开头,以{{ end}}结尾
模板文件代码:
(1).在一个模板文件(hello.html)中定义一个模板
<!--定义模板-->
{{define "model"}}
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
{{template "content"}}
</body>
</html>
{{end}}
(2).在一个模板文件(hello.html)中定义多个模板
<!--定义模板-->
{{define "model"}}
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
{{template "content"}}
</body>
</html>
{{end}}
{{define "content"}}
<a href="#">测试模板文件</a>
{{end}}
处理器端代码:
package main
import(
"html/template"
"net/http"
)
//测试define
func handler(w http.ResponseWriter, r *http.Request) {
//解析模板文件
t := template.Must(template.ParseFiles("hello.html"))
//执行模板
t.ExecuteTemplate(w, "model", "")
}
func main() {
http.HandleFunc("/testdefine", handler)
http.ListenAndServe(":8080",nil)
}
6.块动作
Go1.6引入了一个新的块动作,这个动作允许用户定义一个模板并立即使用,相当于设置了一个默认的模板
//格式
{{block arg}}
如果找不到模板的话,我就显示了喔
{{end}}
模板文件代码:
<!--定义模板-->
{{define "model"}}
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
{{block"content"}}
如果找不到,我就要显示了喔
{{end}}
</body>
</html>
{{end}}
处理器端代码:
package main
import(
"html/template"
"net/http"
)
//测试define
func handler(w http.ResponseWriter, r *http.Request) {
rand.Seed(time.Now().Unix())
var t *template.Template
if rand.lntn(5) > 2 {
//解析模板文件
t := template.Must(template.ParseFiles("hello.html", "content1.html"))
} else {
//解析模板文件
t := template.Must(template.ParseFiles("hello.html"))
}
//执行模板
t.ExecuteTemplate(w, "model", "")
}
func main() {
http.HandleFunc("/testdefine", handler)
http.ListenAndServe(":8080",nil)
}
在不同模板文件中定义同名的模板
hello.html
<!--定义模板-->
{{define "model"}}
<html>
<head>
<title>模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
{{template"content"}}
</body>
</html>
{{end}}
content1.html
<html>
<head>
<title>content1模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
<!--定义content模板-->
{{define "content"}}
<h1>这是content1.html模板文件中的内容</h1>
{{end}}
</body>
</html>
content2.html
<html>
<head>
<title>content2模板文件</title>
<meta charset="utf-8"/>
</head>
<body>
<!--定义content模板-->
{{define "content"}}
<h1>这是content2.html模板文件中的内容</h1>
{{end}}
</body>
</html>
处理器端代码:
package main
import(
"html/template"
"net/http"
)
//测试define
func handler(w http.ResponseWriter, r *http.Request) {
rand.Seed(time.Now().Unix())
var t *template.Template
if rand.lntn(5) > 2 {
//解析模板文件
t := template.Must(template.ParseFiles("hello.html", "content1.html"))
} else {
//解析模板文件
t := template.Must(template.ParseFiles("hello.html", "content2.html"))
}
//执行模板
t.ExecuteTemplate(w, "model", "")
}
func main() {
http.HandleFunc("/testdefine", handler)
http.ListenAndServe(":8080",nil)
}
[上一节][golang Web开发] 3.golang web开发:处理请求