Golang学习笔记(08-2-2-Template)

812 阅读4分钟

1. Template 介绍

Go中提供了两种类型的template包:html/template 和 text/template 。前者用于前端项目,其中对一些特殊字符进行了安全转义操作,避免可能存在的安全问题。后者用于普通文件的渲染,比如 ansible 的清单文件或者k8s的yaml文件等。Template 模板使用语法如下:

  • 模板文件后缀通常为 .tmpl 或者 .tpl,目的是方便区分。文件编码格式为 UTF-8
  • 默认使用 {{ }} 标识变量或者关键字,可以人为修改
  • 使用 . 标识传进来的数据,多个数据传递一般使用map方式,{{ .key }} 访问传递进来的数据中子字段
  • 注释格式为 {{/* xxxx */}} 注释不能嵌套,但是可以多行。

Template 一般使用步骤如下:

  • 定义一个模板文件
  • 解析模板文件
  • 渲染模板文件

2. Template基本语法

2.1. 数据传递

2.1.1. 常见传递的数据类型

在模板语法中, {{ . }} 表示传递个模板的变量,仅支持一个变量。变量常用类型有:字符串,map,结构体和结构体指针,结构体指针在渲染后,也是结构体的值。如下案例,编译的时候需要注意可执行文件的路径,因为代码的模板文件为相对路径。

// templates/data.tmpl
data: {{ . }}
import (
	"fmt"
	"html/template"
	"net/http"
)

var tp *template.Template

func init() {
	var err error
	tp, err = template.ParseFiles("./templates/data.tmpl")
	if err != nil {
		fmt.Printf("template parse failed,err:%s\n", err.Error())
		return
	}
}

func f1(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, "Hello world!"); err != nil {
		_, _ = fmt.Fprint(w, "template operate failed!")
		return
	}
}

func f2(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, map[string]interface{}{
		"name": "ZhangSan",
		"age":  18,
	}); err != nil {
		_, _ = fmt.Fprint(w, "template operate failed!")
		return
	}
}

func f3(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, struct {
		name string
		age  int
	}{
		name: "LiSi",
		age:  27,
	}); err != nil {
		_, _ = fmt.Fprint(w, "template operate failed!")
		return
	}
}

func f4(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, &struct {
		name string
		age  int
	}{
		name: "WanWu",
		age:  36,
	}); err != nil {
		_, _ = fmt.Fprint(w, "template operate failed!")
		return
	}
}

func main() {
	http.HandleFunc("/f1", f1) // 数据为 字符串
	http.HandleFunc("/f2", f2) // 数据为 map
	http.HandleFunc("/f3", f3) //  数据为结构体
	http.HandleFunc("/f4", f4) //  数据为结构体指针
	err := http.ListenAndServe("127.0.0.1:8080", nil)
	if err != nil {
		fmt.Printf("start http server failed, err:%s\n", err.Error())
		return
	}
}
[root@duduniao go_learn]# curl 127.0.0.1:8080/f1
data: Hello world!
[root@duduniao go_learn]# curl 127.0.0.1:8080/f2
data: map[age:18 name:ZhangSan]
[root@duduniao go_learn]# curl 127.0.0.1:8080/f3
data: {LiSi 27}
[root@duduniao go_learn]# curl 127.0.0.1:8080/f4
data: {WanWu 36}

2.1.2. 传递多个数据

在Go的模板中, {{ . }} 表示传递给模板的值,仅支持一个。但需要传递多个值的时候,有来两种方案:传递map或者结构体,采用 {{ .fieldName }} 方式获取字段的值。结构体想要对外可见,必须首字母大写,map根据Key取值时,如果取不到字段则为空,结构体取不到则报错。

// templates/data.tmpl
name: {{ .Name }}
age: {{ .Age }}
import (
	"fmt"
	"html/template"
	"net/http"
)

var tp *template.Template

func init() {
	var err error
	tp, err = template.ParseFiles("./templates/data.tmpl")
	if err != nil {
		fmt.Printf("template parse failed,err:%s\n", err.Error())
		return
	}
}

func f2(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, map[string]interface{}{
		"Name": "ZhangSan",
		"Age":  18,
	}); err != nil {
		_, _ = fmt.Fprint(w, fmt.Sprintf("template operate failed, err:%s", err.Error()))
		return
	}
}

func f3(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, struct {
		Name string
		Age  int
	}{
		Name: "LiSi",
		Age:  27,
	}); err != nil {
		_, _ = fmt.Fprint(w, fmt.Sprintf("template operate failed, err:%s", err.Error()))
		return
	}
}

func f4(w http.ResponseWriter, r *http.Request) {
	if err := tp.Execute(w, &struct {
		Name string
		Age  int
	}{
		Name: "WanWu",
		Age:  36,
	}); err != nil {
		_, _ = fmt.Fprint(w, fmt.Sprintf("template operate failed, err:%s", err.Error()))
		return
	}
}

func main() {
	//http.HandleFunc("/f1", f1) // 数据为 字符串
	http.HandleFunc("/f2", f2) // 数据为 map
	http.HandleFunc("/f3", f3) //  数据为结构体
	http.HandleFunc("/f4", f4) //  数据为结构体指针
	err := http.ListenAndServe("127.0.0.1:8080", nil)
	if err != nil {
		fmt.Printf("start http server failed, err:%s\n", err.Error())
		return
	}
}
[root@duduniao go_learn]# curl 127.0.0.1:8080/f2
name: ZhangSan
age: 18
[root@duduniao go_learn]# curl 127.0.0.1:8080/f3
name: LiSi
age: 27
[root@duduniao go_learn]# curl 127.0.0.1:8080/f4
name: WanWu
age: 36

2.2. 模板中自定义变量

使用 {{ $var := xxx }} 可以定义一个新的变量,引用的时候使用 {{ $var }} ,已经定义过的变量使用 {{ $var = xxx }} 。变量定义的所在行会产生空行,采用 {{- 或者 -}} 去除不必要的空行和空格。

{{- $var01 := . -}}
Name: {{ $var01.Name }}
Age: {{ $var01.Age }}
{{ $var01 = "name: 张三" -}}
{{ $var02 := "password: 123456" -}}
info: {{ $var01}} - {{$var02}}
[root@duduniao go_learn]# curl 127.0.0.1:8080
Name: 张三
Age: 18
info: name: 张三 - password: 123456

2.3. 条件判断

2.3.1. 条件判断常用函数

Golang 的Template将能输出数据的变量或者表达式称为 pipeline,if 条件判断有以下三种格式,和go语言基本一致,只需要注意格式。如果 pipeline 输出为该数据类型的零值则为 false。

{{if pipeline}} T1 {{end}}
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
len     取pipeline的长度
eq      如果arg1 == arg2则返回真
ne      如果arg1 != arg2则返回真
lt      如果arg1 < arg2则返回真
le      如果arg1 <= arg2则返回真
gt      如果arg1 > arg2则返回真
ge      如果arg1 >= arg2则返回真

2.3.2. 条件判断案例

{{ if gt .Age 20 }}{{.Age }}gt than 20{{ else }}{{ .Aget }}le than 20{{ end }}
{{ if .Addr }}Addr:{{.Addr}}{{end}}
{{ if .Name }}Name:{{ .Name }}{{end}}

2.4. 遍历

使用 range 关键词可以实现对数组、切片、map和channel的遍历。遍历的过程中,如果需要取值则用 赋值的语句。

{{- range $k,$v := . -}}
{{- if eq $k "hobby" -}}
{{- range $i, $hobby := $v }}hobby: {{ $hobby }}{{ end }}
{{ else }}
{{ $k }} - {{ $v }}
{{- end }}
{{- end}}