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,结构体和结构体指针,结构体指针在渲染后,也是结构体的值。如下案例,编译的时候需要注意可执行文件的路径,因为代码的模板文件为相对路径。

  1. // templates/data.tmpl
  2. data: {{ . }}
  1. import (
  2. "fmt"
  3. "html/template"
  4. "net/http"
  5. )
  6. var tp *template.Template
  7. func init() {
  8. var err error
  9. tp, err = template.ParseFiles("./templates/data.tmpl")
  10. if err != nil {
  11. fmt.Printf("template parse failed,err:%s\n", err.Error())
  12. return
  13. }
  14. }
  15. func f1(w http.ResponseWriter, r *http.Request) {
  16. if err := tp.Execute(w, "Hello world!"); err != nil {
  17. _, _ = fmt.Fprint(w, "template operate failed!")
  18. return
  19. }
  20. }
  21. func f2(w http.ResponseWriter, r *http.Request) {
  22. if err := tp.Execute(w, map[string]interface{}{
  23. "name": "ZhangSan",
  24. "age": 18,
  25. }); err != nil {
  26. _, _ = fmt.Fprint(w, "template operate failed!")
  27. return
  28. }
  29. }
  30. func f3(w http.ResponseWriter, r *http.Request) {
  31. if err := tp.Execute(w, struct {
  32. name string
  33. age int
  34. }{
  35. name: "LiSi",
  36. age: 27,
  37. }); err != nil {
  38. _, _ = fmt.Fprint(w, "template operate failed!")
  39. return
  40. }
  41. }
  42. func f4(w http.ResponseWriter, r *http.Request) {
  43. if err := tp.Execute(w, &struct {
  44. name string
  45. age int
  46. }{
  47. name: "WanWu",
  48. age: 36,
  49. }); err != nil {
  50. _, _ = fmt.Fprint(w, "template operate failed!")
  51. return
  52. }
  53. }
  54. func main() {
  55. http.HandleFunc("/f1", f1) // 数据为 字符串
  56. http.HandleFunc("/f2", f2) // 数据为 map
  57. http.HandleFunc("/f3", f3) // 数据为结构体
  58. http.HandleFunc("/f4", f4) // 数据为结构体指针
  59. err := http.ListenAndServe("127.0.0.1:8080", nil)
  60. if err != nil {
  61. fmt.Printf("start http server failed, err:%s\n", err.Error())
  62. return
  63. }
  64. }
  1. [root@duduniao go_learn]# curl 127.0.0.1:8080/f1
  2. data: Hello world!
  3. [root@duduniao go_learn]# curl 127.0.0.1:8080/f2
  4. data: map[age:18 name:ZhangSan]
  5. [root@duduniao go_learn]# curl 127.0.0.1:8080/f3
  6. data: {LiSi 27}
  7. [root@duduniao go_learn]# curl 127.0.0.1:8080/f4
  8. data: {WanWu 36}

2.1.2. 传递多个数据

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

  1. // templates/data.tmpl
  2. name: {{ .Name }}
  3. age: {{ .Age }}
  1. import (
  2. "fmt"
  3. "html/template"
  4. "net/http"
  5. )
  6. var tp *template.Template
  7. func init() {
  8. var err error
  9. tp, err = template.ParseFiles("./templates/data.tmpl")
  10. if err != nil {
  11. fmt.Printf("template parse failed,err:%s\n", err.Error())
  12. return
  13. }
  14. }
  15. func f2(w http.ResponseWriter, r *http.Request) {
  16. if err := tp.Execute(w, map[string]interface{}{
  17. "Name": "ZhangSan",
  18. "Age": 18,
  19. }); err != nil {
  20. _, _ = fmt.Fprint(w, fmt.Sprintf("template operate failed, err:%s", err.Error()))
  21. return
  22. }
  23. }
  24. func f3(w http.ResponseWriter, r *http.Request) {
  25. if err := tp.Execute(w, struct {
  26. Name string
  27. Age int
  28. }{
  29. Name: "LiSi",
  30. Age: 27,
  31. }); err != nil {
  32. _, _ = fmt.Fprint(w, fmt.Sprintf("template operate failed, err:%s", err.Error()))
  33. return
  34. }
  35. }
  36. func f4(w http.ResponseWriter, r *http.Request) {
  37. if err := tp.Execute(w, &struct {
  38. Name string
  39. Age int
  40. }{
  41. Name: "WanWu",
  42. Age: 36,
  43. }); err != nil {
  44. _, _ = fmt.Fprint(w, fmt.Sprintf("template operate failed, err:%s", err.Error()))
  45. return
  46. }
  47. }
  48. func main() {
  49. //http.HandleFunc("/f1", f1) // 数据为 字符串
  50. http.HandleFunc("/f2", f2) // 数据为 map
  51. http.HandleFunc("/f3", f3) // 数据为结构体
  52. http.HandleFunc("/f4", f4) // 数据为结构体指针
  53. err := http.ListenAndServe("127.0.0.1:8080", nil)
  54. if err != nil {
  55. fmt.Printf("start http server failed, err:%s\n", err.Error())
  56. return
  57. }
  58. }
  1. [root@duduniao go_learn]# curl 127.0.0.1:8080/f2
  2. name: ZhangSan
  3. age: 18
  4. [root@duduniao go_learn]# curl 127.0.0.1:8080/f3
  5. name: LiSi
  6. age: 27
  7. [root@duduniao go_learn]# curl 127.0.0.1:8080/f4
  8. name: WanWu
  9. age: 36

2.2. 模板中自定义变量

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

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

2.3. 条件判断

2.3.1. 条件判断常用函数

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

  1. {{if pipeline}} T1 {{end}}
  2. {{if pipeline}} T1 {{else}} T0 {{end}}
  3. {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
  1. len pipeline的长度
  2. eq 如果arg1 == arg2则返回真
  3. ne 如果arg1 != arg2则返回真
  4. lt 如果arg1 < arg2则返回真
  5. le 如果arg1 <= arg2则返回真
  6. gt 如果arg1 > arg2则返回真
  7. ge 如果arg1 >= arg2则返回真

2.3.2. 条件判断案例

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

2.4. 遍历

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

  1. {{- range $k,$v := . -}}
  2. {{- if eq $k "hobby" -}}
  3. {{- range $i, $hobby := $v }}hobby: {{ $hobby }}{{ end }}
  4. {{ else }}
  5. {{ $k }} - {{ $v }}
  6. {{- end }}
  7. {{- end}}