html/template包实现了数据驱动的模板,用于生成可对抗代码注入的安全HTML输出。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用text/template包。

模板

在基于MVC的Web架构中,我们通常需要在后端渲染一些数据到HTML文件中,从而实现动态的网页效果

模板示例

通过将模板应用于一个数据结构(即该数据结构作为模板的参数)来执行,来获得输出。模板中的注释引用数据接口的元素(一般如结构体的字段或者字典的键)来控制执行过程和获取需要呈现的值。模板执行时会遍历结构并将指针表示为’.‘(称之为”dot”)指向运行过程中数据结构的当前位置的值。

用作模板的输入文本必须是utf-8编码的文本。”Action”—数据运算和控制单位—由”“界定;在Action之外的所有文本都不做修改的拷贝到输出中。Action内部不能有换行,但注释可以有换行。

HTML文件代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>Hello {{.}}</p>
</body>
</html>

我们的HTTP server端代码如下:

func main() {
http.HandleFunc("/", SayHello)
err := http.ListenAndServe(":8000", nil)
if err != nil {
fmt.Println(err.Error())
return
}
}
func SayHello(w http.ResponseWriter, r *http.Request) {
// 解析指定文件,生成模板对象
tmpl, err := template.ParseFiles("./templates/index.html")
if err != nil {
fmt.Println("create template failed, err: ", err)
return
}
// 利用给定数据渲染模板,并将结果写入w
tmpl.Execute(w, "sankuan")
}

模板语法

{{.}}

模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。

当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:

func main() {
http.HandleFunc("/", SayHello)
if err := http.ListenAndServe(":8000", nil); err != nil {
fmt.Println(err.Error())
return
}
}
func SayHello(w http.ResponseWriter, r *http.Request) {
// 解析模板文件,生成模板对象
tmpl, err := template.ParseFiles("./templates/index.html")
if err != nil {
fmt.Println(err.Error())
return
}
// 将数据渲染到模板,并写入到w
user := User{"张三", 18, true}
tmpl.Execute(w, &user)
}
type User struct {
Name string
Age uint8
Gender bool
}

HTML文件代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>Name: {{.Name}}</p>
<p>Age: {{.Age}}</p>
<p>Gender: {{.Gender}}</p>
</body>
</html>

同理,当我们传入的变量是map时,也可以在模板文件中通过.根据key来取值。

注释

<p>Gender: {{/*.Gender*/}}</p>

注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。

pipeline

pipeline是指产生数据的操作。比如{{.}}、{{.Name}}等。Go的模板语法中支持使用管道符号|链接多个命令,用法和unix下的管道类似:|前面的命令会将运算结果(或返回值)传递给后一个命令的最后一个位置。

注意 : 并不是只有使用了|才是pipeline。Go的模板语法中,pipeline的概念是传递数据,只要能产生数据的,都是pipeline。

html中定义变量

Action里可以初始化一个变量来捕获管道的执行结果。初始化语法如下:

<body>
<p>Name: {{.Name}}</p>
<p>Age: {{.Age}}</p>
<p>Gender: {{.Gender}}</p> {{$score := 88}}
<p>分数: {{$score}}</p>
</body>

if判断

{{if lt .Age 18}}未成年{{else}}已经成年{{end}}

golang的模板也支持if的条件判断,当前支持最简单的bool类型和字符串类型的判断

{{if .condition}} {{end}}

  当.condition为bool类型的时候,则为true表示执行,当.condition为string类型的时候,则非空表示执行。

当然也支持else , else if嵌套

{{if .condition1}} {{else if .contition2}} {{end}}

假设我们需要逻辑判断,比如与或、大小不等于等判断的时候,我们需要一些内置的模板函数来做这些工作,目前常用的一些内置模板函数有:

not 非

{{if not .condition}}

{{end}}

and 与

{{if and .condition1 .condition2}}

{{end}}

or 或

{{if or .condition1 .condition2}}

{{end}}

eq 等于

{{if eq .var1 .var2}}

{{end}}

ne 不等于

{{if ne .var1 .var2}}

{{end}}

lt 小于 (less than)

{{if lt .var1 .var2}}

{{end}}

le 小于等于

{{if le .var1 .var2}}

{{end}}

gt 大于

{{if gt .var1 .var2}}

{{end}}

ge 大于等于

{{if ge .var1 .var2}}

{{end}}

循环

main.go

func main() {
http.HandleFunc("/", SayHello)
if err := http.ListenAndServe(":8000", nil); err != nil {
fmt.Println(err.Error())
return
}
}
func SayHello(w http.ResponseWriter, r *http.Request) {
// 解析模板文件,生成模板对象
tmpl, err := template.ParseFiles("./templates/index.html")
if err != nil {
fmt.Println(err.Error())
return
}
// 将数据渲染到模板,并写入到w
user := User{"哈哈", 88, false, []int{11, 22, 33}, map[string]interface{}{
"name": "定时发送放得开了",
"age": 998,
}}
tmpl.Execute(w, &user)
}
type User struct {
Name string
Age uint8
Gender bool
Score []int
Height map[string]interface{}
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
{{ .Name }}
{{ .Score }} {{range .Score}}
{{.}}
{{end}}
{{range $i, $v := .Score}}
{{$i}}---{{$v}}
{{end}} {{range .Height}}
{{.}}
{{end}}
{{range $k, $v := .Height}}
{{$k}}---{{$v}}
{{end}}
</body>
</html>

模板的嵌套

在编写模板的时候,我们常常将公用的模板进行整合,比如每一个页面都有导航栏和页脚,我们常常将其编写为一个单独的模块,让所有的页面进行导入,这样就不用重复的编写了。

任何网页都有一个主模板,然后我们可以在主模板内嵌入子模板来实现模块共享。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body> <!--定义子模板-->
{{define "navbar"}}
<h1>啊哈哈哈</h1>
<p>嘿嘿</p>
{{end}} <!--使用子模板-->
{{template "navbar" .}} </body>
</html>

模板中使用函数

golang的模板其实功能很有限,很多复杂的逻辑无法直接使用模板语法来表达,所以只能使用模板函数来绕过。

  首先,template包创建新的模板的时候,支持.Funcs方法来将自定义的函数集合导入到该模板中,后续通过该模板渲染的文件均支持直接调用这些函数。

该函数集合的定义为:

type FuncMap map[string]interface{}

key为方法的名字,value则为函数。这里函数的参数个数没有限制,但是对于返回值有所限制。有两种选择,一种是只有一个返回值,还有一种是有两个返回值,但是第二个返回值必须是error类型的。这两种函数的区别是第二个函数在模板中被调用的时候,假设模板函数的第二个参数的返回不为空,则该渲染步骤将会被打断并报错。

在模板文件内,调用方法也非常的简单:

{{funcname .arg1 .arg2}}

假设我们定义了一个函数

func add(left int, right int) int

则在模板文件内,通过调用

{{add 1 2}}

就可以获得取值: 3

这个结果,golang的预定义函数没有add,所以有点儿麻烦。

main.py

func main() {
http.HandleFunc("/", SayHello)
if err := http.ListenAndServe(":8000", nil); err != nil {
fmt.Println(err.Error())
return
}
}
func SayHello(w http.ResponseWriter, r *http.Request) {
// 解析模板文件,生成模板对象
tmpl := template.New("./templates/index.html")
tmpl.Funcs(template.FuncMap{
"add": Add,
})
tmpl, err := tmpl.ParseFiles("./templates/index.html")
if err != nil {
fmt.Println(err.Error())
return
}
// 将数据渲染到模板,并写入到w
user := User{"哈哈", 88, false, []int{11, 22, 33}, map[string]interface{}{
"name": "定时发送放得开了",
"age": 998,
}}
if err := tmpl.ExecuteTemplate(w, "index.html", user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
} //tmpl.Execute(w, &user)
}
type User struct {
Name string
Age uint8
Gender bool
Score []int
Height map[string]interface{}
}
func Add(a, b int) int {
return a + b
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body> 哈哈哈哈 {{add 11 22}} </body>
</html>

预定义函数

执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用Funcs方法添加函数到模板里。

预定义的全局函数如下:

and
函数返回它的第一个empty参数或者最后一个参数;
就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
返回第一个非empty参数或者最后一个参数;
亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
返回它的单个参数的布尔值的否定
len
返回它的参数的整数类型长度
index
执行结果为第一个参数以剩下的参数为索引/键指向的值;
如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print
即fmt.Sprint
printf
即fmt.Sprintf
println
即fmt.Sprintln
html
返回其参数文本表示的HTML逸码等价表示。
urlquery
返回其参数文本表示的可嵌入URL查询的逸码等价表示。
js
返回其参数文本表示的JavaScript逸码等价表示。
call
执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);
其中Y是函数类型的字段或者字典的值,或者其他类似情况;
call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;
如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;

比较函数

布尔函数会将任何类型的零值视为假,其余视为真。

下面是定义为函数的二元比较运算的集合:

    eq      如果arg1 == arg2则返回真
ne 如果arg1 != arg2则返回真
lt 如果arg1 < arg2则返回真
le 如果arg1 <= arg2则返回真
gt 如果arg1 > arg2则返回真
ge 如果arg1 >= arg2则返回真

为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:

{{eq arg1 arg2 arg3}}

比较函数只适用于基本类型(或重定义的基本类型,如”type Celsius float32”)。但是,整数和浮点数不能互相比较。

golang中的标准库template的更多相关文章

  1. golang中的标准库数据格式

    数据格式介绍 是系统中数据交互不可缺少的内容 这里主要介绍JSON.XML.MSGPack JSON json是完全独立于语言的文本格式,是k-v的形式 name:zs 应用场景:前后端交互,系统间数 ...

  2. golang中的标准库context

    在 Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理.请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库和RPC服务.用来处理一个请 ...

  3. golang中的标准库log

    Go语言内置的log包实现了简单的日志服务.本文介绍了标准库log的基本使用. 使用Logger log包定义了Logger类型,该类型提供了一些格式化输出的方法.本包也提供了一个预定义的" ...

  4. golang中的标准库context解读

    简介 golang 中的创建一个新的 goroutine , 并不会返回像c语言类似的pid,所有我们不能从外部杀死某个goroutine,所有我就得让它自己结束,之前我们用 channel + se ...

  5. golang中的标准库http

    Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现. http客户端 基本的HTTP/HTTPS请求 Get.Head.Post和PostForm函数发出HTTP/HTTP ...

  6. golang中的标准库IO操作

    参考链接 输入输出的底层原理 终端其实是一个文件,相关实例如下: os.Stdin:标准输入的文件实例,类型为*File os.Stdout:标准输出的文件实例,类型为*File os.Stderr: ...

  7. golang中的标准库time

    时间类型 time.Time类型表示时间.我们可以通过time.Now()函数获取当前的时间对象,然后获取时间对象的年月日时分秒等信息.示例代码如下: func main() { current := ...

  8. golang中的标准库反射

    反射 反射是指程序在运行期对程序本身访问和修改的能力 变量的内在机制 变量包含类型信息和值信息 var arr [10]int arr[0] = 10 类型信息:是静态的元信息,是预先定义好的 值信息 ...

  9. golang中的标准库strconv

    strconv 包 strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi().Itia().parse系列.format系列.append系列. string与int ...

随机推荐

  1. 基于 SoC 的卷积神经网络车牌识别系统设计(1)概述

    NOTES: 这是第三届全国大学生集成电路创新创业大赛 - Arm 杯 - 片上系统设计挑战赛(本人指导的一个比赛).主要划分为以下的 Top5 重点.难点.亮点.热点以及创新点:1.通过 Arm C ...

  2. c++内存分布之虚析构函数

    关于 本文代码演示环境: VS2017+32程序 虚析构函数是一种特殊的虚函数,可以知道,虚函数影响的内存分布规律应该也适用虚析构函数.看看实际结果. Note,一个类中,虚析构函数只能有一个. 本文 ...

  3. 【LeetCode】17. Letter Combinations of a Phone Number 电话号码的字母组合

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:电话号码, 字母组合,回溯法,题解,leetcode, ...

  4. P1629八

    P1629八 Accepted 标签:[显示标签]     描述 八是个很有趣的数字啊.八=发,八八=爸爸,88=拜拜.当然最有趣的还是8用二进制表示是1000.怎么样,有趣吧.当然题目和这些都没有关 ...

  5. JVM 内存布局

    JVM 内存布局规定了 Java 在运行过程中内存申请.分配.管理的策略,保证了 JVM 的高效稳定运行. 线程是否共享 Heap (堆区) 堆是 OOM 故障最主要的发生区域.它是内存区域中最大的一 ...

  6. Momentum and NAG

    目录 Momentum Nesterov accelerated gradient NESTEROV 的另外一个方法? Momentum Momentum的迭代公式为: \[v_t = \gamma ...

  7. Java_Swing中关于关闭窗口的方法

    注意:在继承Jframe的过程中,由于工具的快捷功能可以会继承到Frame类,继承到Jframe类打开的窗口默认是可以关闭的,而Frame类打开的窗口点了关闭的没有反应的. 1.关闭子窗口后,父窗口也 ...

  8. 《手把手教你》系列技巧篇(五十三)-java+ selenium自动化测试-上传文件-上篇(详细教程)

    1.简介 在实际工作中,我们进行web自动化的时候,文件上传是很常见的操作,例如上传用户头像,上传身份证信息等.所以宏哥打算按上传文件的分类对其进行一下讲解和分享. 2.为什么selenium没有提供 ...

  9. mysql 语句中 sum函数求和 null 变 0

    https://blog.csdn.net/Z_passionate/article/details/83821039

  10. html 基础 vscode的常用快捷键

    1.ctrl+/ //注释代码 2.文件内容查找替换:ctrl+f ctrl+h ,替换一个ctrl+shift+1,替换所有ctrl+alt+enter 3.移动当前行,向上alt+up(方向键↑) ...