go web开发学习笔记之模板学习

入门demo

1. 定义模板

hello.html

<html>
<head>
<title>模板文件</title>
</head>
<body>
hello {{.}}
</body>
</html>

2. 解析模板

3. 渲染模板

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)
	
}

4. 访问结果

在这里插入图片描述

5. 总结

模板渲染其实就是将后台的数据替换模板文件中的特定字符,这里的特定字符是模板文件中的{{.}}。

模板语法

模板中的{{.}},这其中的 " . "代表传入模板的对象。

(1)如果传入的是一个struct结构体对象,那么可以通过 " .属性 "的方式取出属性值

  1. 新建模板
    user.html
<html>
<head>
<title>模板文件</title>
</head>
<body>
		用户名:{{.Username}}
		年龄: {{.Age}}
		地址:{{.Address}}
</body>
</html>
  1. 模板解析和模板渲染

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)函数

函数分为预定义函数、比较函数、自定义函数。
  1. 预定义函数就是模板中预定义好的函数,函数列表如下:
    在这里插入图片描述
  2. 比较函数。比较函数会将任何数据零值视为假,其余视为真。比较函数列表如下:
    在这里插入图片描述
  3. 自定义函数。go语言的模板支持自定义函数。

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 := "<script>alert(111)</script>"
	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 := "<script>alert(111)</script>"
	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 := "<a href = 'http://www.baidu.com'>百度</a>"
	//此时,我们想在页面上留下百度的跳转链接,但是这样写的话,肯定会被转义
	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 := "<a href = 'http://www.baidu.com'>百度</a>"
	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)
}

访问的结果是
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值