Golang template 使用样例

Go标准库提供了几个package可以产生输出结果,而text/template 提供了基于模板输出文本内容的功能。html/template 则是产生 安全的HTML格式的输出。

01 text/template样例

Golang text/template 包是一个数据驱动的模版渲染工具。提供条件判断,数组或map遍历;参数赋值,函数或方法调用;自定义函数扩展,模板嵌套及重用等功能。基于该工具,可以轻松实现复杂场景的文本渲染。如Helm Template 基于此实现了功能强大的Kubernetes配置文件渲染工作。

package main

import (
	"os"
	"strings"
	"text/template"
)

const text = `
{{/* This is a zoo template */}}
{{with .Name}}Welcome to {{.}}{{end}}
There are {{len .Animals}} animals, they are: 
{{range .Animals}}
{{- . | upper -}},
{{end}}
{{if gt (len .Zookeepers) 0}}
There are {{len .Zookeepers}} zookeepers, they are:
{{range $no, $name := .Zookeepers}}
{{printf "%03d" $no}}: {{$name -}}
{{end}}
{{end}}
{{block "Welcome" .Name}}You're welcome to visit {{.}} next time!{{end}}
`

type Zoo struct {
	Name       string
	Animals    []string
	Zookeepers map[int]string
}

func main() {
	// template
	tpl := template.Must(template.New("zoo").Funcs(template.FuncMap{
		"upper": func(s string) string { // self-defined functions
			return strings.ToUpper(s)
		},
	}).Parse(text))

	// zookeepers
	zooKeepers := map[int]string{
		0: "Alan",
		1: "Larry",
		2: "Alice",
	}

	// zoo
	zoo := &Zoo{
		"Beijing Zoo",
		[]string{"elephant", "tiger", "dolphin"},
		zooKeepers,
	}

	// execute
	tpl.Execute(os.Stdout, zoo)
}

运行结果

Welcome to Beijing Zoo
There are 3 animals, they are: 
ELEPHANT,
TIGER,
DOLPHIN,


There are 3 zookeepers, they are:

000: Alan
001: Larry
002: Alice

You're welcome to visit Beijing Zoo next time!

02 html/template样例

以下内容主要来自:Go标准库:Go template用法详解

入门示例

先看一个例子,以下为test.html文件的内容,里面使用了一个template语法{{.}}

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>Go Web</title>
	</head>
	<body>
		{{ . }}
	</body>
</html>

以下是test.html同目录下的一个go web程序:

package main

import (
	"html/template"
	"net/http"
)

func tmpl(w http.ResponseWriter, r *http.Request) {
	t1, err := template.ParseFiles("test.html")
	if err != nil {
		panic(err)
	}
	t1.Execute(w, "hello world")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/tmpl", tmpl)
	server.ListenAndServe()
}

前面的html文件中使用了一个template的语法{{.}},这部分是需要通过go的template引擎进行解析,然后替换成对应的内容。

在go程序中,handler函数中使用template.ParseFiles("test.html"),它会自动创建一个模板(关联到变量t1上),并解析一个或多个文本文件(不仅仅是html文件),解析之后就可以使用Execute(w,"hello world")去执行解析后的模板对象,执行过程是合并、替换的过程。例如上面的{{.}}中的.会替换成当前对象"hello world",并和其它纯字符串内容进行合并,最后写入w中,也就是发送到浏览器"hello world"。

本文不解释这些template包的函数、方法以及更底层的理论知识,本文只解释template的语法,如果觉得这些无法理解,或者看不懂官方手册,请看深入剖析Go template

关于点“.”和作用域

在写template的时候,会经常用到"."。比如{{.}}{{len .}}{{.Name}}{{$x.Name}}等等。

在template中,点".“代表当前作用域的当前对象。它类似于java/c++的this关键字,类似于perl/python的self。如果了解perl,它更可以简单地理解为默认变量$_

例如,前面示例test.html中{{.}},这个点是顶级作用域范围内的,它代表Execute(w,"hello worold")的第二个参数"hello world”。也就是说它代表这个字符串对象。

再例如,有一个Person struct。

type Person struct {
	Name string
	Age  int
}

func main(){
	p := Person{"longshuai",23}
	tmpl, _ := template.New("test").Parse("Name: {{.Name}}, Age: {{.Age}}")
	_ = tmpl.Execute(os.Stdout, p)
}

这里{{.Name}}{{.Age}}中的点".“代表的是顶级作用域的对象p,所以Execute()方法执行的时候,会将{{.Name}}替换成p.Name,同理{{.Age}}替换成{{p.Age}}

但是并非只有一个顶级作用域,range、with、if等内置action都有自己的本地作用域。它们的用法后文解释,这里仅引入它们的作用域来解释”."。

例如下面的例子,如果看不懂也没关系,只要从中理解".“即可。

package main

import (
	"os"
	"text/template"
)

type Friend struct {
	Fname string
}
type Person struct {
	UserName string
	Emails   []string
	Friends  []*Friend
}

func main() {
	f1 := Friend{Fname: "xiaofang"}
	f2 := Friend{Fname: "wugui"}
	t := template.New("test")
	t = template.Must(t.Parse(
`hello {{.UserName}}!
{{ range .Emails }}
an email {{ . }}
{{- end }}
{{ with .Friends }}
{{- range . }}
my friend name is {{.Fname}}
{{- end }}
{{ end }}`))
	p := Person{UserName: "longshuai",
		Emails:  []string{"a1@qq.com", "a2@gmail.com"},
		Friends: []*Friend{&f1, &f2}}
	t.Execute(os.Stdout, p)
}

输出结果

hello longshuai!

an email a1@qq.com
an email a2@gmail.com

my friend name is xiaofang
my friend name is wugui

这里定义了一个Person结构,它有两个slice结构的字段。在Parse()方法中:

  • 顶级作用域的{{.UserName}}{{.Emails}}{{.Friends}}中的点都代表Execute()的第二个参数,也就是Person对象p,它们在执行的时候会分别被替换成p.UserName、p.Emails、p.Friends。
  • 因为Emails和Friend字段都是可迭代的,在{{range .Emails}}...{{end}}这一段结构内部an email {{.}},这个”.“代表的是range迭代时的每个元素对象,也就是p.Emails这个slice中的每个元素。
  • 同理,with结构内部{{range .}}的”.“代表的是p.Friends,也就是各个,再此range中又有一层迭代,此内层{{.Fname}}的点代表Friend结构的实例,分别是&f1&f2,所以{{.Fname}}代表实例对象的Fname字段。

剩余内容可以去原文 查看。

02 参考资料