• Go Web——template标准库



    简介

    template包实现了数据驱动的用于生成文本输出的模板。

    生成html文件的模板在html/template包下。

    模板使用插值语法{{.var}}格式,也可以使用一些流程控制,例如if else、循环range,还可以使用一些函数,包括内建函数和自定义函数

    一 快速入门

    package main
    
    import (
    	"os"
    	"text/template"
    )
    
    func main() {
    	// 数据
    	name := "Psych"
    	// 定义模板
    	muban := "hello,{{.}}"
    	// 解析模板
    	tmpl, err := template.New("test").Parse(muban)
    	if err != nil {
    		panic(err)
    	}
    	// 执行模板并输出到终端
    	err = tmpl.Execute(os.Stdout, name)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    hello,Psych
    [Done] exited with code=0 in 1.472 seconds
    
    • 1
    • 2
    • 3

    二 传入字符串

    定义模板文件hello.html

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>HELLOtitle>
    head>
    <body>
        <p>hello {{ . }}p>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试:

    package main
    
    import (
    	"net/http"
    	"text/template"
    )
    
    func sayHello(w http.ResponseWriter, r *http.Request) {
    	//解析模板
    	t, err := template.ParseFiles("./hello.html")
    	if err != nil {
    		panic(err)
    	}
    	//渲染模板
    	err = t.Execute(w, "Psych")
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	http.HandleFunc("/", sayHello)
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    浏览器输入127.0.0.1:9090显示结果:

    在这里插入图片描述

    三 传入结构体

    定义模板文件hello.html

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>HELLOtitle>
    head>
    <body>
        <p>hello {{ .Name }},Your age {{ .Age }}p>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试:

    package main
    
    import (
    	"net/http"
    	"text/template"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func sayHello(w http.ResponseWriter, r *http.Request) {
    	//解析模板
    	t, err := template.ParseFiles("./hello.html")
    	if err != nil {
    		panic(err)
    	}
    	//渲染模板
    	user1 := User{
    		Name: "Psych",
    		Age:  18,
    	}
    	err = t.Execute(w, user1)
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	http.HandleFunc("/", sayHello)
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    浏览器输入127.0.0.1:9090显示结果:

    在这里插入图片描述

    四 传入多个结构体

    定义模板文件hello.html

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>HELLOtitle>
    head>
    <body>
        <p>hello {{ .user1.Name }},Your age {{ .user1.Age }}p>
        <p>hello {{ .user2.Name }},Your age {{ .user2.Age }}p>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试:

    package main
    
    import (
    	"net/http"
    	"text/template"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func sayHello(w http.ResponseWriter, r *http.Request) {
    	//解析模板
    	t, err := template.ParseFiles("./hello.html")
    	if err != nil {
    		panic(err)
    	}
    	//渲染模板
    	user1 := User{
    		Name: "Psych",
    		Age:  18,
    	}
    
    	user2 := User{
    		Name: "Klee",
    		Age:  999,
    	}
    
    	err = t.Execute(w, map[string]interface{}{
    		"user1": user1,
    		"user2": user2,
    	})
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	http.HandleFunc("/", sayHello)
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    浏览器输入127.0.0.1:9090显示结果:

    在这里插入图片描述

    五 文本与空格

    模板引擎在进行替换的时候,是完全按照文本格式进行替换的。除了需要评估和替换的地方,所有的行分隔符、空格等空白都原样保留。

    所以,对于要解析的内容,不要随意缩进、随意换行

    例如:

    {{23}} < {{45}}			-> 23 < 45 
    {{23}} < {{- 45}}		-> 23 <45 
    {{23 -}} < {{45}}		-> 23< 45 
    {{23 -}} < {{- 45}}		-> 23<45 
    
    • 1
    • 2
    • 3
    • 4

    有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样;

    这个时候可以:

    • 使用{{- xxx语法去除模板内容左侧的所有空白符号
    • 使用 xxx -}}去除模板内容右侧的所有空白符号。

    注意:-要紧挨{{}}同时与模板值之间需要使用空格分隔

    六 模板注释

    注释方式:{{/* a comment */}}

    注释后的内容不会被引擎进行替换。

    注意:注释行在替换的时候也会占用行,所以应该去除前缀后后缀空白,否则会多一行

    {{- /* a comment */}}
    {{/* a comment */ -}}
    {{- /* a comment */ -}}
    
    • 1
    • 2
    • 3

    七 管道Pipeline

    管道就是一系列命令的链式调用。当然,也可以是一个命令。例如:计算表达式的值{{.}}{{.Name}}或者是一个函数调用或者方法调用。

    可以使用管道符号|连接多个命令,用法和unix下的管道类似:|前面的命令将运算结果(或者返回值)传递给后一个命令的最后一个位置。

    注意:并非只有使用了|才是管道。Go template中,管道的概念是传递数据,只要能产生数据的都是管道。

    Pipeline的几种示例,他们都输出"Psych"

    {{`"Psych"`}}
    {{printf "%q" "Psych"}}
    {{"Psych" | printf "%q"}}
    {{printf "%q" (print "Psy" "ch")}}
    {{"ch" | printf "%s%s" "Psy" | printf "%q"}}
    {{"Psych" | printf "%s" | printf "%q"}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试:

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>testtitle>
    head>
    <body>
        {{`"Psych"`}} <br>
        {{printf "%q" "Psych"}} <br>
        {{"Psych" | printf "%q"}} <br>
        {{printf "%q" (print "Psy" "ch")}} <br>
        {{"ch" | printf "%s%s" "Psy" | printf "%q"}} <br>
        {{"Psych" | printf "%s" | printf "%q"}} <br>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:

    在这里插入图片描述

    八 变量

    • 未定义过的变量:$var := pipeline
    • 已定义过的变量:$var = pipeline

    示例:

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>testtitle>
    head>
    <body>
        {{$Name := "Psych"}}
        {{$Name = "Klee"}}
    
        {{$Name}} <br>
    
        {{$len := (len "hello,golang")}}
    
        {{$len}} <br>
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行结果:

    在这里插入图片描述

    九 if

    Go模板语法中的条件判断有以下几种:

    {{if pipeline}} T1 {{end}}
    
    {{if pipeline}} T1 {{else}} T0 {{end}}
    
    {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
    
    {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    pipeline为false的情况是各种数据对象的0值:数值0;指针或接口是nil;数组、slice、map或string则是len为0。

    经常结合一下运算符表达式使用:

    eq      如果arg1 == arg2则返回真
    ne      如果arg1 != arg2则返回真
    lt      如果arg1 < arg2则返回真
    le      如果arg1 <= arg2则返回真
    gt      如果arg1 > arg2则返回真
    ge      如果arg1 >= arg2则返回真
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    arg1==arg2 || arg1==arg3 || arg1==arg4 ...
    
    • 1

    (和go的||不一样,不做惰性运算,所有参数都会执行)

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

    示例:

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>testtitle>
    head>
    <body>
        {{$Age := 18}}
    
        {{if (ge $Age 18)}}
        <h3>你是成年人h3>
        {{else}}
        <h3>你还未成年h3>
        {{end}}
        
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:

    在这里插入图片描述

    十 range

    Go的模板语法中使用range关键字进行遍历,有以下两种写法,其中pipeline的值必须是数组、切片、字典或者通道。

    {{range pipeline}} T1 {{end}}
    如果pipeline的值为0值,不会有任何输出
    
    {{range pipeline}} T1 {{else}} T0 {{end}}
    如果pipeline的值为0值,则会执行T0。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    range可以迭代slice、数组、map或channel。

    迭代时,会设置.为当前正在迭代的元素。

    示例:

    test.html

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>testtitle>
    head>
    <body>
        
        {{range $x := . -}}
            {{println $x}}
        {{- end}}
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    test.go

    package main
    
    import (
    	"net/http"
    	"text/template"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func sayHello(w http.ResponseWriter, r *http.Request) {
    	//解析模板
    	t, err := template.ParseFiles("./hello.html")
    	if err != nil {
    		panic(err)
    	}
    	//渲染模板
    	s := []string{"四川", "成都", "高新"}
    
    	err = t.Execute(w, s)
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	http.HandleFunc("/", sayHello)
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:

    在这里插入图片描述

    十一 with

    with用来设置"."的值,语法如下:

    {{with pipeline}} T1 {{end}}
    如果pipeline不为0值时,"."设置为pipeline运算的值,否则跳过。
    
    {{with pipeline}} T1 {{else}} T0 {{end}}
    如果pipeline为0值,执行T0,否则"."设置为pipeline运算的值并执行T1。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    {{with "四川-成都-高新"}}{{println .}}{{end}}
    
    • 1

    运行结果:

    四川-成都-高新
    
    • 1

    十二 内置函数

    执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用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,模板执行会中断并返回给调用模板执行者该错误;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

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

    十三 嵌套template:define和template

    define可以直接在待解析内容中定义一个模板,这个模板会加入到common结构组中,并关联到关联名称上

    {{template "name"}}
    {{template "name" pipeline}}
    {{define "name"}}
    
    • 1
    • 2
    • 3

    实例演示

    假设有一个header.html、footer.html和index.html,其中index.html包含header.html和footer.html

    header.html:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
    
        {{define "header"}}
        <p>header部分...p>
        {{end}}
        
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    footer.html:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
    
        {{define "footer"}}
        <p>footer部分...p>
        {{end}}
        
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    index.html:

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    <body>
        
        {{template "header"}}
        <h1>首页h1>
        {{template "footer"}}
    
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    test.go:

    package main
    
    import (
    	"net/http"
    	"text/template"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func tmpl(w http.ResponseWriter, r *http.Request) {
    	//解析模板
    	t, err := template.ParseFiles("index.html", "header.html", "footer.html")
    	if err != nil {
    		panic(err)
    	}
    	//渲染模板
    	err = t.Execute(w, nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	http.HandleFunc("/", tmpl)
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果:

    在这里插入图片描述

    十四 text/template与html/tempalte的区别

    html/template针对的是需要返回HTML内容的场景,在模板渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。

    这个时候传入一段JS代码并使用html/template去渲染该文件,会在页面上显示出转义后的JS内容。 这就是html/template为我们做的事。

    但是在某些场景下,我们如果相信用户输入的内容,不想转义的话,可以自行编写一个safe函数,手动返回一个template.HTML类型的内容。示例如下:

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Documenttitle>
    head>
    <body>
    <div>{{ .str1}}div>
    <div>{{ .str2 | safe}}div>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    func xss(w http.ResponseWriter, r *http.Request) {
    
       t, err := template.New("xss.tmpl").Funcs(template.FuncMap{
          "safe": func(str string) template.HTML {
             return template.HTML(str)
          },
       }).ParseFiles("./xss.tmpl")
       if err != nil {
          fmt.Println("parse template failed err =", err)
          return
       }
    
       str1 := ""
       str2 := "百度"
       t.Execute(w, map[string]string{
          "str1": str1,
          "str2": str2,
       })
    }
    

    这样我们只需要在模板文件不需要转义的内容后面使用我们定义好的safe函数就可以了。

    {{ . | safe }}
    
    • 1
  • 相关阅读:
    .net core添加SQL日志输出
    java计算机毕业设计竞赛信息发布及组队系统源码+数据库+lw文档+系统
    【Linux】 rm命令使用
    【NoSQL】NoSQL之redis配置与优化(简单操作)
    认识NIO
    06在IDEA中创建Java和Web工程,了解不同工程下的类路径,在IDEA中执行Maven命令
    使用Docker安装和部署kkFileView
    谁懂万方检索的高级检索嘛
    C++11 多线程 (上)
    力扣每日一题63:不同路径||
  • 原文地址:https://blog.csdn.net/qq_39280718/article/details/126469042