hello.html
<html>
<head>
<title>模板文件title>
head>
<body>
hello {{.}}
body>
html>
package main
import(
"fmt"
"net/http"
"html/template"
)
func sayHello(w http.ResponseWriter,r *http.Request){
//解析模板
t,err:=template.ParseFiles("hello.html")
if err != nil{
fmt.Println("模板解析异常,err=",err)
return
}
//模板渲染
err = t.Execute(w,"woaini")
if err != nil{
fmt.Println("模板渲染异常,err=",err)
return
}
}
func main(){
http.HandleFunc("/hello",sayHello)
http.ListenAndServer(":8080",nil)
}
模板渲染其实就是将后台的数据替换模板文件中的特定字符,这里的特定字符是模板文件中的{{.}}。
模板中的{{.}},这其中的 " . "代表传入模板的对象。
(1)如果传入的是一个struct结构体对象,那么可以通过 " .属性 "的方式取出属性值
<html>
<head>
<title>模板文件title>
head>
<body>
用户名:{{.Username}}
年龄: {{.Age}}
地址:{{.Address}}
body>
html>
model.User.go
type User struct{
Username string
Age int64
Address string
}
package main
import (
"fmt"
"net/http"
"html/template"
""
)
func getUser(w http.ResponseWriter,r *http.Request){
//解析模板
t,err := template.ParseFiles("user.html")
if err != nil{
fmt.Println("解析模板异常 err = ",err)
return
}
//生成响应数据
user := User{
Username:"zhangsan",
Age : 20,
Address:"周家屯",
}
//渲染模板
err = t.Execute(w,user)
if err != nil{
fmt.Println("模板渲染异常 err = " ,err)
return
}
}
func main(){
http.HandleFunc("/user",getUser)
http.ListenAndServe(":8081",nil)
}
(2)同理,如果传进来的是一个map对象,同样可以在模板中通过" .key "的方式来取值
模板文件map.html
<html>
<head>
<title>模板文件title>
head>
<body>
用户名:{{.Username}}
年龄: {{.Age}}
地址:{{.Address}}
body>
html>
package main
import (
"fmt"
"net/http"
"html/template"
""
)
func getUser(w http.ResponseWriter,r *http.Request){
//解析模板
t,err := template.ParseFiles("user.html")
if err != nil{
fmt.Println("解析模板异常 err = ",err)
return
}
//生成响应数据
mapDemo := map[string]interface{}{
"Username":"小天鹅",
"Age":20,
"Address":"临福路",
}
//渲染模板
err = t.Execute(w,mapDemo)
if err != nil{
fmt.Println("模板渲染异常 err = " ,err)
return
}
}
func main(){
http.HandleFunc("/user",getUser)
http.ListenAndServe(":8082",nil)
}
结果:
(3)pipeline:管道符。go语言中只要产生了数据就是pipeline。
(4)条件判断
go语言中条件判断有以下几种:
{{if pipeline}} T1 {{end}}
{{if pipeline }} T1 {{else}} T2 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T2 {{end}}
(5)range :遍历
go模板语法中使用range关键字进行遍历,其中pipeline必须是数组、切片、字典或者是通道
{{range pipeline}} T1 {{end}}
{{range pipeline}} T1 {{else }} T2 {{end}}
模板文件 list.html
<html>
<head>
<title>
woaini
title>
head>
<body>
{{range .}}
{{.}}
{{end}}
body>
html>
package main
import (
"fmt"
"net/http"
"html/template"
)
func getList(w http.ResponseWriter,r *http.Request){
myList := []string{
"篮球",
"足球",
"双色球",
}
}
func main(){
http.HandleFunc("list",getList)
http.ListenAndServe(":8083",nil)
}
结果
(6)with 。创建一个区域,区域内的 " . "代表with 后的pipeline.
{{with .}} T1 {{end}}
{{with .}} T1 {{else }} T2 {{end}}
(7)函数
函数分为预定义函数、比较函数、自定义函数。
define.html
<html>
<head>
<title>
自定义函数
title>
head>
<body>
{{love .}}
body>
html>
package main
import (
"fmt"
"net/http"
"html/template"
)
func selfDefine(w http.ResponseWriter,r *http.Request){
//定义模板
//定义方法 ,模板方法如果有返回error的话,则error必须是最后一个参数
love := func(a string)string{
return a + " is a handsome boy!"
}
//创建模板
t := template.New("define.html")
//将方法添加到模板,必须先将方法添加到模板后才能解析模板
t.Funcs(template.FuncMap{
"love":love,
})
//解析模板
_,err := template.ParseFiles("define.html")
if err != nil{
fmt.Println("解析模板异常 err = ",err)
return
}
//渲染模板
t.Execute(w,"lijiahui")
}
func main(){
http.HandleFunc("/selfDefine",selfDefine)
http.ListenAndServe(":8085",nil)
}
(8)模板继承。一些html页面可能除了少数数据域不同外,其他部分基本相同。此时,我们定义一个父模板,然后定义子模板继承它,就可以实现页面的继承关系,减少代码量。
parent.html
<html>
<head>
<title>
父模板
title>
head>
<body>
// block关键字来定义继承的模板名字“content”, 后面跟的“.”则代表传入子模板的数据
{{block "kid" .}}
{{end}}
body>
html>
kid1.html
//template关键字继承父模板,后面跟父模板的名字,“ . ”代表传进入子模板的数据
{{template "parent.html" .}}
{{define "kid"}}
this is kid1.html
hello {{.}}
{{end}}
kid2.html
//template关键字继承父模板,后面跟父模板的名字,“ . ”代表传进入子模板的数据
{{template "parent.html" .}}
{{define "kid"}}
this is kid2.html
hello {{.}}
{{end}}
main.go
package main
import (
"fmt"
"net/http"
"html/template"
)
func Kid1(w http.ResponseWriter,r *http.Request){
t,err := template.ParseFiles("parent.html","kid1.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
t.ExecuteTemplate(w,"kid1.html","lijiahui")
}
func Kid2(w http.ResponseWriter,r *http.Request){
t,err := template.ParseFiles("parent.html","kid2.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
t.ExecuteTemplate(w,"kid2.html","lijiahui")
}
func main(){
http.HandleFunc("/kid1",Kid1)
http.HandleFunc("/kid2",kid2)
http.ListenAndServe(":8087",nil)
}
结果:
父模板上的内容不需要改变,只需要改变子模板或者添加子模板就能达到修改或者增加页面的目的。
(9)自定义标签
自定义标签就是当 “{{” 或者 "}}"在页面中已经被使用了,我们采用自己定义的标签来替代它们。
replace.html
<html>
<head>
<title>
replace
title>
head>
<body>
采用了替代标签 {[ . ]}
body>
html>
main.go
package main
import (
"fmt"
"net/http"
"html/template"
)
func replace(w http.ResponseWriter,r *http.Request){
t,err := template.New("replace.html").Delim("{[","]}").ParseFiles("replace.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
t.Execute(w,"woaini")
}
func main(){
http.HandleFunc("/replace",replace)
http.ListenAndServe(":8087",nil)
}
执行结果:
(10)text/template和html/template的区别
二者的区别在于html/template 可以对数据进行转义,保证页面信息安全,避免xss攻击。看下面的例子
xss1.html
<html>
<head>
<title>
xss1.html
title>
head>
<body>
{{.}}
body>
html>
main.go
package main
import (
"fmt"
"net/http"
"html/template"
)
func xss1(w http.ResponseWriter,r *http.Request){
t,err := template.ParseFiles("xss1.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
str1 := ""
t.Execute(w,str)
}
func xss2(w http.ResponseWriter,r *http.Request){
t,err := template.ParseFiles("xss1.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
str := ""
t.Execute(w,str)
}
func main(){
http.HandleFunc("/xss1",xss1)
http.HandleFunc("/xss2",xss2)
http.ListenAndServe(":8088",nil)
}
浏览器访问 http://localhost:8088/xss1 结果为:
此时,js脚本被当作字符显示在body中。如果此时将main.go中导入的包html/template换成text/template,那么重新访问的结果就是:
此时,js脚本会被浏览器解析执行,也就是说此时并没有被转义。
所以,在选用这两个包的时候,如果涉及到网页,则采用html/template会更加安全一些
可是此时又有一个问题出现了, 如果我们采用html/template包后,所有的传入页面的内容都会被转义,可是假如我们需要某个内容不被转义该怎么办呢? 看下面例子:
xss2.html
<html>
<head>
<title>
xss2.html
title>
head>
<body>
{{.}}
body>
<html>
main.go
package main
import (
"fmt"
"net/http"
"html/template"
)
func xss2(w http.ResponseWriter,r *http.Request){
t,err := template.ParseFiles("xss2.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
str := "百度"
//此时,我们想在页面上留下百度的跳转链接,但是这样写的话,肯定会被转义
t.Execute(w,str)
}
func main(){
http.HandleFunc("/xss2",xss2)
http.ListenAndServe(":8088",nil)
}
访问 http://localhost:8088/xss2的结果是:
此时结果被转义,而我们不想它转义,这里可以采用自定义的模板方法来实现,在main.go中编写方法
func xss3(w http.ResponseWriter,r *http.Request){
//为模板添加自定义方法,该方法的作用是将字符串转换成html页面返回
t,err:= template.New("xss2.html").Func(
template.FuncMap{
"safe":func (s string) template.HTML{
return template.HTML(s)
},
},
).ParseFiles("xss2.html")
if err != nil{
fmt.Println("模板解析异常 err = ",err)
return
}
str := "百度"
t.Execute(w,str)
}
在 main方法中注册xss3方法
func main(){
http.HandleFunc("/xss2",xss2)
http.HandleFunc("/xss3",xss3)
http.ListenAndServe(":8088",nil)
}
修改xss2.html页面如下
<html>
<head>
<title>
xss2.html
title>
head>
<body>
{{ // 使用自定义的模板方法}}
{{ safe .}}
body>
<html>
访问http://localhost:8088/xss3的结果为
(11)嵌入模板
embed.html
<html>
<head>
<title>
embed.html
title>
head>
<body>
{{template "embed1"}}
{{template "embed2.html"}}
hello {{.}}
body>
html>
{{define "embed1"}}
<ol>
<li>吃饭li>
<li>睡觉li>
<li>打豆豆li>
ol>
{{end}}
embed2.html
<ol>
<li>篮球li>
<li>足球li>
<li>双色球li>
ol>
package main
import (
"fmt"
"net/http"
"html/template"
)
func embed(w http.ResponseWriter,r *http.Request){
t,err := template.ParseFiles("embed.html","embed2.html")
if err != nil{
fmt.Println("解析模板异常 err = ",err)
return
}
t.Execute(w,"lijiahui")
}
func main(){
http.HandleFunc("/embed",embed)
http.ListenAndServe(":8089",nil)
}