go标准库的学习-text/template

参考:https://studygolang.com/pkgdochtml

导入方式:golang

import "text/template"

template包实现了数据驱动的用于生成文本输出的模板。其实简单来讲就是将一组文本嵌入另外一组文本模版中,返回一个你指望的文本数组

若是要生成HTML格式的输出,参见html/template包,该包提供了和本包相同的接口,但会自动将输出转化为安全的HTML格式输出,能够抵抗一些网络攻击。安全

用做模板的输入文本必须是utf-8编码的文本。"Action",即数据运算和控制单位由"{{"和"}}"界定(即{{Action}});在Action以外的全部文本都不作修改的拷贝到输出中。Action内部不能有换行,但注释能够有换行。网络

{{Action}}中的运算能够经过()进行分组,如:并发

//执行结果能够访问其字段或者键对应的值:
print (.F1 arg1) (.F2 arg2)
(.StructValuedMethod "arg").Field

 

经解析生成模板后,一个模板能够安全的并发执行。ide

 

有两个经常使用的传入参数的类型:函数

  • 一个是struct,在模板内能够读取该struct域的内容来进行渲染。下面的例子中使用更多的是这种方法,即自定义一个struct,而后将其值做为Execute()函数的第二个参数,而后.就表示该对象,而后经过它来调用相应的字符串值,甚至是函数
  • 一个是map[string]interface{},在模板内可使用key来进行渲染

type Template

type Template struct {
    *parse.Tree
    // 内含隐藏或非导出字段
}

表明一个解析好的模板,*parse.Tree字段仅仅是暴露给html/template包使用的,所以其余人应该视字段未导出。post

func New

func New(name string) *Template

建立一个名为name的模板。ui

func (*Template) Parse

func (t *Template) Parse(text string) (*Template, error)

Parse方法将字符串text解析为模板。嵌套定义的模板会关联到最顶层的t。Parse能够屡次调用,但只有第一次调用能够包含空格、注释和模板定义以外的文本。若是后面的调用在解析后仍剩余文本会引起错误、返回nil且丢弃剩余文本;若是解析获得的模板已有相关联的同名模板,会覆盖掉原模板

func (*Template) Execute

func (t *Template) Execute(wr io.Writer, data interface{}) (err error)

Execute方法将解析好的模板应用到data上,并将输出写入wr。若是执行时出现错误,会中止执行,但有可能已经写入wr部分数据。模板能够安全的并发执行。

 

1.调用的是变量时

1)举一个最简单的例子,传入的是string字符串:

package main 
import(
    "os"
    "text/template"
)

func main() {
    str := "world"
    tmpl, err := template.New("test").Parse("hello, {{.}}\n") //创建一个名字为test的模版"hello, {{.}}"
    if err != nil{
        panic(err)
    }
    err = tmpl.Execute(os.Stdout, str) //将str的值合成到tmpl模版的{{.}}中,并将合成获得的文本输入到os.Stdout,返回hello, world
    if err != nil{
        panic(err)
    }
}
{{.}}:此标签输出当前对象的值,在这里即表明str对象,可是在下面的例子中,.表明的是Execute中传入的sweaters对象的值
 

2)另外一个例子,传入的是struct对象的值:

package main 
import(
    "os"
    "text/template"
)

type Inventory struct {
    Material string
    Count    uint
}

func main() {
    sweaters := Inventory{"wool", 17}
    tmpl, err := template.New("test").Parse("{{.Count}} of {{.Material}}\n")//{{.Count}}获取的是struct对象中的Count字段的值
    if err != nil { panic(err) }
    err = tmpl.Execute(os.Stdout, sweaters)//返回 17 of wool
    if err != nil { panic(err) }
}

若是上面的例子中Count的值也是一个struct对象,可使用{{.Count.Field1}}来访问其字段

 

3)自定义的变量,能够先看下面的方法的例子

举例:

package main 
import(
    "os"
    "text/template" ) type MyMethod struct{ Say string Name string } func (my *MyMethod)SayHello() string{//没参数 return "world" } func (my *MyMethod)SayYouName(name string) string { //有参数 return "my name is : " + name } func main() { mine := &MyMethod{ Say : "hello", Name : "student"} //先对变量$str1,$str2,$str3赋值,一个是直接将字符串值赋值,另两个是调用函数,将返回值赋值,而后再将变量值输出 tmpl, err := template.New("test").Parse("{{$str1 := .Say}}{{$str2 := .SayHello}}{{$str3 := .SayYouName .Name}}{{$str1}} {{$str2}}\n{{$str3}}\n") if err != nil { panic(err) } err = tmpl.Execute(os.Stdout, mine) if err != nil { panic(err) } }

返回:

bogon:~ user$ go run testGo.go 
hello world
my name is : student

 

 

2.函数:

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

方法必须有一到两个返回值,若是是两个,那么第二个必定是error接口类型

1)模版内定义函数

举例:

package main 
import(
    "os"
    "text/template"
)
type MyMethod struct{
    Name string
}

func (my *MyMethod)SayHello() string{//没参数
    return "hello world"
}

func (my *MyMethod)SayYouName(name string) string { //有参数
    return "my name is : " + name
}

func main() {
    mine := &MyMethod{ Name : "boss"}
    tmpl, err := template.New("test").Parse("{{.SayHello}}\n{{.SayYouName .Name}}\n")
    if err != nil { 
        panic(err) 
    }
    err = tmpl.Execute(os.Stdout, mine)
    if err != nil { 
        panic(err)
    }
}

返回:

bogon:~ user$ go run testGo.go 
hello world
my name is : boss

 

2)使用Funcs方法添加函数到模板

func (*Template) Funcs

func (t *Template) Funcs(funcMap FuncMap) *Template

Funcs方法向模板t的函数字典里加入参数funcMap内的键值对。若是funcMap某个键值对的值不是函数类型或者返回值不符合要求会panic。可是,能够对t函数列表的成员进行重写。方法返回t以便进行链式调用。

type FuncMap

type FuncMap map[string]interface{}

FuncMap类型定义了函数名字符串到函数的映射,每一个函数都必须有1到2个返回值,若是有2个则后一个必须是error接口类型;若是有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用者该错误。

举例:

package main 
import(
    "os"
    "text/template"
)


func SayHello() string{//没参数
    return "hello world"
}

func SayYouName(name string) string { //有参数
    return "my name is : " + name
}

func main() {
    funcMap := template.FuncMap{
        //在FuncMap中声明相应要使用的函数,而后就可以在template字符串中使用该函数
        "SayHello" : SayHello,
        "SayYouName" : SayYouName,
    }
    name := "boss"
    tmpl, err := template.New("test").Funcs(funcMap).Parse("{{SayHello}}\n{{SayYouName .}}\n")
    if err != nil { 
        panic(err) 
    }
    err = tmpl.Execute(os.Stdout, name)
    if err != nil { 
        panic(err)
    }
}

返回:

bogon:~ user$ go run testGo.go 
hello world
my name is : boss

根据pipeline的定义,上面的{{SayYouName .}}等价于{{.|SayYouName}}

预约义的全局函数以下:

就是在{{}}中能够直接使用的函数

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,模板执行会中断并返回给调用模板执行者该错误;

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

举例:

package main 
import(
    "os"
    "text/template"
)

func main() {
    name := "boss"
    tmpl, err := template.New("test").Parse(`{{printf "%q\n" .}}`)
    if err != nil { 
        panic(err) 
    }
    err = tmpl.Execute(os.Stdout, name)
    if err != nil { 
        panic(err)
    }
}

返回:

bogon:~ user$ go run testGo.go 
"boss"

 

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

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

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

arg1==arg2 || arg1==arg3 || arg1==arg4 ...

(和go的||不同,不作惰性运算,全部参数都会执行)

比较函数只适用于基本类型(或重定义的基本类型,如"type Celsius float32")。它们实现了go语言规则的值的比较,但具体的类型和大小会忽略掉,所以任意类型有符号整数值均可以互相比较;任意类型无符号整数值均可以互相比较;等等。可是,整数和浮点数不能互相比较

 

3.pipelines

这上面举的全部例子中的{{ }}内的操做咱们将其称做pipelines

pipeline一般是将一个command序列分割开,再使用管道符'|'链接起来(但不使用管道符的command序列也能够视为一个管道),上面的例子都是最简单的pipelines的类型,由于一个{{}}中只有一个command。而上面自定义变量中的语法为:

$variable := pipeline

更复杂的有:

range $index, $element := pipeline

这时,$index和$element分别设置为数组/切片的索引或者字典的键,以及对应的成员元素。注意这和go range从句只有一个参数时设置为索引/键不一样!

一个变量的做用域只到声明它的控制结构("if"、"with"、"range")的"end"为止,若是不是在控制结构里声明会直到模板结束为止。子模板的调用不会从调用它的位置(做用域)继承变量。

若是没有定义变量的名字,而是只使用$,那么在模板开始执行时,$会设置为传递给Execute方法的参数,就是说,dot的初始值。

在一个链式的pipeline里,每一个command的结果都做为下一个command的最后一个参数。pipeline最后一个command的输出做为整个管道执行的结果。

command的输出能够是1到2个值,若是是2个后一个必须是error接口类型。若是error类型返回值非nil,模板执行会停止并将该错误返回给执行模板的调用者。

Actions 

下面是一个action(动做)的列表。"Arguments"和"pipelines"表明数据的执行结果,细节定义在后面。

{{/* a comment */}}
    注释,执行时会忽略。能够多行。注释不能嵌套,而且必须紧贴分界符始止,就像这里表示的同样。
{{pipeline}}
    pipeline的值的默认文本表示会被拷贝到输出里。
{{if pipeline}} T1 {{end}}
    若是pipeline的值为empty,不产生输出,不然输出T1执行结果。不改变dot的值。
    Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
{{if pipeline}} T1 {{else}} T0 {{end}}
    若是pipeline的值为empty,输出T0执行结果,不然输出T1执行结果。不改变dot的值。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
    用于简化if-else链条,else action能够直接包含另外一个if;等价于:
        {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
{{range pipeline}} T1 {{end}}
    pipeline的值必须是数组、切片、字典或者通道。
    若是pipeline的值其长度为0,不会有任何输出;
    不然dot依次设为数组、切片、字典或者通道的每个成员元素并执行T1;
    若是pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。
{{range pipeline}} T1 {{else}} T0 {{end}}
    pipeline的值必须是数组、切片、字典或者通道。
    若是pipeline的值其长度为0,不改变dot的值并执行T0;不然会修改dot并执行T1。
{{template "name"}}
    执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""
{{template "name" pipeline}}
    执行名为name的模板,提供给模板的参数为pipeline的值。
{{with pipeline}} T1 {{end}}
    若是pipeline为empty不产生输出,不然将dot设为pipeline的值并执行T1。不修改外面的dot。
{{with pipeline}} T1 {{else}} T0 {{end}}
    若是pipeline为empty,不改变dot并执行T0,不然dot设为pipeline的值并执行T1。

 

4.条件判断-if

{{if pipeline}} T1 {{end}}
    若是pipeline的值为empty,不产生输出,不然输出T1执行结果。不改变dot的值。
    Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
{{if pipeline}} T1 {{else}} T0 {{end}}
    若是pipeline的值为empty,输出T0执行结果,不然输出T1执行结果。不改变dot的值。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
    用于简化if-else链条,else action能够直接包含另外一个if;等价于:
        {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}

将其与全局函数结合使用为:

{{if not .condition}} 
{{end}}
{{if and .condition1 .condition2}} //即若是condition1成立则返回condition2,不然返回condition1
{{end}}
{{if or .condition1 .condition2}}  //即若是condition1成立则返回condition1,不然返回condition2
{{end}}
{{if eq .var1 .var2}} 
{{end}}
...

还有:

{{with pipeline}} T1 {{end}}
    若是pipeline为empty不产生输出,不然将dot设为pipeline的值并执行T1。不修改外面的dot。
{{with pipeline}} T1 {{else}} T0 {{end}}
    若是pipeline为empty,不改变dot并执行T0,不然dot设为pipeline的值并执行T1。

func Must

func Must(t *Template, err error) *Template

Must函数用于包装返回(*Template, error)的函数/方法调用,它会在err非nil时panic,通常用于变量初始化:

var t = template.Must(template.New("name").Parse("text"))

这样就不用像上面的例子同样还要使用if err != nil来判断是否出错

举例:

package main 
import(
    "os"
    "text/template"
    "log"
)

func main() {
    //建立一个模版
    const letter = `
Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with .Gift -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
`
    type Recipient struct {
        Name, Gift string
        Attended   bool
    }
    var recipients = []Recipient{
        {"Aunt Mildred", "bone china tea set", true},
        {"Uncle John", "moleskin pants", false},
    }

    // Create a new template and parse the letter into it.
    t := template.Must(template.New("letter").Parse(letter))

    // Execute the template for each recipient.
    for _, r := range recipients {
        err := t.Execute(os.Stdout, r)
        if err != nil {
            log.Println("executing template:", err)
        }
    }
}

返回:

bogon:~ user$ go run testGo.go 

Dear Aunt Mildred,

It was a pleasure to see you at the wedding.
Thank you for the lovely bone china tea set.

Best wishes,
Josie

Dear Uncle John,

It is a shame you couldn't make it to the wedding.
Thank you for the lovely moleskin pants.

Best wishes,
Josie

 注意:

  • 在{{- else}}、{{- end}}和{{with .Gift -}}中的-表示消除{{else}}等会致使的空行
  • {{with .Gift}}表示若是Gift不为空的话,则打印下面的句子

 

5.遍历-range

{{range pipeline}} T1 {{end}}
    pipeline的值必须是数组、切片、字典或者通道。
    若是pipeline的值其长度为0,不会有任何输出;
    不然dot依次设为数组、切片、字典或者通道的每个成员元素并执行T1;
    若是pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。
{{range pipeline}} T1 {{else}} T0 {{end}}
    pipeline的值必须是数组、切片、字典或者通道。
    若是pipeline的值其长度为0,即没有可遍历的值时,不改变dot的值并执行T0;不然会修改dot并执行T1。

常见用法有:

{{range $i, $v := .Var}} //显示获得遍历的index和value
{{$i}} => {{$v}} 
{{end}} 

{{range .Var}} //没有显示去获取遍历获得的index和value,这时候要得到value值,使用{{.}}表示
{{.}} 
{{end}} 

{{range .slice}} //若是想要在range...end中访问非遍历获得的value,即外部的其余值,则在前面添加$来表示
{{$.OutsideContent}}
{{end}}

举例:

package main 
import(
    "os"
    "text/template"
    "log"
)

func main() {
    //建立一个模版
    rangeTemplate := `
{{if .Kind}}
{{range $i, $v := .MapContent}}
{{$i}} => {{$v}} , {{$.OutsideContent}}
{{end}}
{{else}}
{{range .MapContent}}
{{.}} , {{$.OutsideContent}}
{{end}}    
{{end}}`

    str1 := []string{"this is the first range", "use its index and value"}
    str2 := []string{"this is the second range", "do not use its index and value"}

    type Content struct {
        MapContent []string
        OutsideContent string
        Kind bool
    }
    var contents = []Content{
        {str1, "this is the first outside content", true},
        {str2, "this is the second outside content", false},
    }

    // Create a new template and parse the letter into it.
    t := template.Must(template.New("range").Parse(rangeTemplate))

    // Execute the template for each recipient.
    for _, c := range contents {
        err := t.Execute(os.Stdout, c)
        if err != nil {
            log.Println("executing template:", err)
        }
    }
}

返回:

bogon:~ user$ go run testGo.go 



0 => this is the first range , this is the first outside content

1 => use its index and value , this is the first outside content




this is the second range , this is the second outside content

do not use its index and value , this is the second outside content

模版回车

上面的空行与模版中的回车有关,若是想要没有输出的空行,上面的模版应该写成:

    rangeTemplate := `{{if .Kind}}{{range $i, $v := .MapContent}}
{{$i}} => {{$v}} , {{$.OutsideContent}}
{{end}}
{{else}}{{range .MapContent}}
{{.}} , {{$.OutsideContent}}
{{end}}    
{{end}}`

 

6.模版嵌套

{{template "name"}}
    执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""。固然首先要使用{{define "name"}}{{end}}定义好该模版
{{template "name" pipeline}}
    执行名为name的模板,提供给模板的参数为pipeline的值。将管道的值赋给子模板中的"."(即"{{.}}"),即{{template "name" .}}

1)使用{{define "name"}}...{{end}}定义模版

举例1:

package main 
import(
    "os"
    "text/template"
    "log"
)

func main() {
    //建立一个模版
    templateContent := `{{define "T1"}}ONE{{end}}{{define "T2"}}TWO{{end}}{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}{{template "T3"}}`

    // Create a new template and parse the letter into it.
    t := template.Must(template.New("template").Parse(templateContent))

    // Execute the template for each recipient.
    err := t.Execute(os.Stdout, nil)
    if err != nil {
        log.Println("executing template:", err)
    }
}

返回:

bogon:~ user$ go run testGo.go 
ONE TWObogon:~ user$ 

2)使用template.New("name")定义模版

 

举例2:

等价于上面的例子,只是写法不一样

package main 
import(
    "os"
    "text/template"
    "log"
)

func main() {
    //建立一个模版
    template1 := "ONE"
    template2 := "TWO"
    template3 := `{{template "T1"}} {{template "T2"}}`

    // Create a new template and parse the letter into it.
    t := template.Must(template.New("T1").Parse(template1))
    t = template.Must(t.New("T2").Parse(template2))
    t = template.Must(t.New("T3").Parse(template3))

    // Execute the template for each recipient.
    err := t.Execute(os.Stdout, nil)
    if err != nil {
        log.Println("executing template:", err)
    }
}

返回:

bogon:~ user$ go run testGo.go 
ONE TWObogon:~ user$ 

 

7.多模版

其实在上面的模版嵌套中咱们就使用了多模版的概念

func (*Template) New

func (t *Template) New(name string) *Template

New方法建立一个和t关联的名字为name的模板并返回它。这种能够传递的关联容许一个模板使用template action调用另外一个模板。

func (*Template) Lookup

func (t *Template) Lookup(name string) *Template

Lookup方法返回与t关联的名为name的模板,若是没有这个模板返回nil。

func (*Template) Templates

func (t *Template) Templates() []*Template

Templates方法返回与t相关联的模板的切片,包括t本身。

当一个Template中有多个模版时,你须要指定解析的模版,所以在这里使用的是ExecuteTemplate,而不是Execute

func (*Template) Name

func (t *Template) Name() string

返回模板t的名字。

func (*Template) ExecuteTemplate

func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

ExecuteTemplate方法相似Execute,可是使用名为name的t关联的模板产生输出。

 举例:

package main 
import(
    "os"
    "text/template"
    "fmt"
)

type Inventory struct {
    Material string
    Count    uint
}

func main() {
    sweaters := Inventory{"wool", 17}
    template1 := "{{.Count}} of {{.Material}}\n"
    template2 := "{{.Material}} of {{.Count}}\n"

    tmpl := template.Must(template.New("T1").Parse(template1))
    fmt.Println(tmpl.Name()) //T1
    tmpl = template.Must(tmpl.New("T2").Parse(template2))
    fmt.Println(tmpl.Name()) //T2

    err := tmpl.ExecuteTemplate(os.Stdout, "T1", sweaters)//返回 17 of wool
    if err != nil { panic(err) }
    err = tmpl.ExecuteTemplate(os.Stdout, "T2", sweaters)//返回 wool of 17
    if err != nil { panic(err) }

    tmpl = tmpl.Lookup("T1")
    fmt.Println(tmpl.Name()) //T1

    mapTemplate := tmpl.Templates()
    for _, v := range mapTemplate{ //先获得T2,再获得T1
        fmt.Println(v.Name())
    }

}

返回:

bogon:~ user$ go run testGo.go 
T1
T2
17 of wool
wool of 17
T1
T2
T1

 

8.文件模版

其实就是你能够将你的模版内容写到文件当中,而后再从文件中调用

func ParseFiles

func (t *Template) ParseFiles(filenames ...string) (*Template, error)

ParseFiles函数建立一个模板并解析filenames指定的文件里的模板定义。返回的模板的名字是第一个文件的文件名(不含扩展名),内容为解析后的第一个文件的内容。至少要提供一个文件。若是发生错误,会中止解析并返回nil。

func ParseGlob

func (t *Template) ParseGlob(pattern string) (*Template, error)

ParseGlob建立一个模板并解析匹配pattern的文件(参见glob规则)里的模板定义。返回的模板的名字是第一个匹配的文件的文件名(不含扩展名),内容为解析后的第一个文件的内容。至少要存在一个匹配的文件。若是发生错误,会中止解析并返回nil。ParseGlob等价于使用匹配pattern的文件的列表为参数调用ParseFiles。

1)ParseFiles—一个模版文件

ParseFiles接受一个字符串,字符串的内容是一个模板文件的路径(绝对路径or相对路径) 

将模版写到文件templateContent.txt中:

{{.Count}} of {{.Material}}

例子:

package main 
import(
    "os"
    "text/template"
)

type Inventory struct {
    Material string
    Count    uint
}

func main() {
    sweaters := Inventory{"wool", 17}

    tmpl, err := template.ParseFiles("templateContent.txt")//这里不须要使用new(),由于会默认使用文件名来命名
    if err != nil { panic(err) }
    err = tmpl.Execute(os.Stdout, sweaters)//返回 17 of wool
    if err != nil { panic(err) }
}

返回:

bogon:~ user$ cat templateContent.txt
{{.Count}} of {{.Material}}bogon:~ user$ 
bogon:~ user$ go run testGo.go 
17 of woolbogon:~ user$ 

2)ParseGlob—多个模版文件

ParseGlob是用正则的方式匹配多个文件,如当你要选取某文件夹下的全部txt模版文件时,就能够调用ParseGlob("*.txt")

好比修改上面多模版的例子:

templateContent.txt(回车一行来实现换行)

{{.Count}} of {{.Material}}

anotherTemplate.txt(回车一行来实现换行)

{{.Material}} of {{.Count}}

举例:

package main 
import(
    "os"
    "text/template"
    "fmt"
)

type Inventory struct {
    Material string
    Count    uint
}

func main() {
    sweaters := Inventory{"wool", 17}

    tmpl := template.Must(template.ParseGlob("*.txt"))

    mapTemplate := tmpl.Templates()
    for _, v := range mapTemplate{ //先获得anotherTemplate.txt,再获得templateContent.txt
        fmt.Println(v.Name())
    }

    err := tmpl.ExecuteTemplate(os.Stdout, "templateContent.txt", sweaters)//返回 17 of wool
    if err != nil { panic(err) }
    err = tmpl.ExecuteTemplate(os.Stdout, "anotherTemplate.txt", sweaters)//返回 wool of 17
    if err != nil { panic(err) }

}

返回:

bogon:~ user$ cat templateContent.txt
{{.Count}} of {{.Material}}
bogon:~ user$ cat anotherTemplate.txt 
{{.Material}} of {{.Count}}
bogon:~ user$ go run testGo.go 
anotherTemplate.txt
templateContent.txt
17 of wool
wool of 17

 

9.其余

func (*Template) Clone

func (t *Template) Clone() (*Template, error)

Clone方法返回模板的一个副本,包括全部相关联的模板。模板的底层表示树并未拷贝,而是拷贝了命名空间,所以拷贝调用Parse方法不会修改原模板的命名空间。Clone方法用于准备模板的公用部分,向拷贝中加入其余关联模板后再进行使用。

例子:

// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
    // T0.tmpl is a plain template file that just invokes T1.
    {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
    // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
    {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)
// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")
// Here starts the example proper.
// Load the drivers.
drivers := template.Must(template.ParseGlob(pattern))
// We must define an implementation of the T2 template. First we clone
// the drivers, then add a definition of T2 to the template name space.
// 1. Clone the helper set to create a new name space from which to run them.
first, err := drivers.Clone()
if err != nil {
    log.Fatal("cloning helpers: ", err)
}
// 2. Define T2, version A, and parse it.
_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
if err != nil {
    log.Fatal("parsing T2: ", err)
}
// Now repeat the whole thing, using a different version of T2.
// 1. Clone the drivers.
second, err := drivers.Clone()
if err != nil {
    log.Fatal("cloning drivers: ", err)
}
// 2. Define T2, version B, and parse it.
_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
if err != nil {
    log.Fatal("parsing T2: ", err)
}
// Execute the templates in the reverse order to verify the
// first is unaffected by the second.
err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
if err != nil {
    log.Fatalf("second execution: %s", err)
}
err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
if err != nil {
    log.Fatalf("first: execution: %s", err)
}

这样对first或second的操做都不会影响drivers,first和second之间也互不影响

返回:

T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))

 

func (*Template) Delims

func (t *Template) Delims(left, right string) *Template

Delims方法用于设置action的分界字符串,应用于以后的Parse、ParseFiles、ParseGlob方法。嵌套模板定义会继承这种分界符设置。空字符串分界符表示相应的默认分界符:{{或}}。返回值就是t,以便进行链式调用。

默认的分界符即left = {{ , right = }}

若是把分界符改为<>,举例:

package main 
import(
    "os"
    "text/template"
)

func main() {
    str := "world"
    tmpl, err := template.New("test").Delims("<",">").Parse("hello, <.>\n") //创建一个名字为test的模版"hello, <.>}"
    if err != nil{
        panic(err)
    }
    err = tmpl.Execute(os.Stdout, str) //将str的值合成到tmpl模版的{{.}}中,并将合成获得的文本输入到os.Stdout,返回hello, world
    if err != nil{
        panic(err)
    }
}

返回:

bogon:~ user$ go run testGo.go 
hello, world

 

func HTMLEscape

func HTMLEscape(w io.Writer, b []byte)

函数向w中写入b的HTML转义等价表示。

func HTMLEscapeString

func HTMLEscapeString(s string) string

返回s的HTML转义等价表示字符串。

func HTMLEscaper

func HTMLEscaper(args ...interface{}) string

函数返回其全部参数文本表示的HTML转义等价表示字符串。

 举例:

package main 
import(
    "fmt" "net/http" "log" "text/template" ) func index(w http.ResponseWriter, r *http.Request){ r.ParseForm() //解析URL传递的参数,对于POST则解析响应包的主体(request body),若是不调用它则没法获取表单的数据  fmt.Println(r.Form) fmt.Println(r.PostForm) fmt.Println("path", r.URL.Path) fmt.Println("scheme", r.URL.Scheme) fmt.Println(r.Form["url_long"]) //若是使用的是方法FormValue()方法(它只会返回同名参数slice中的第一个,不存在则返回空字符串),则能够不用调用上面的ParseForm()方法 for k, v := range r.Form{ fmt.Println("key :", k) fmt.Println("value :", v) } fmt.Fprintf(w, "hello world") //将html写到w中,w中的内容将会输出到客户端中 } func login(w http.ResponseWriter, r *http.Request){ fmt.Println("method", r.Method) //得到请求的方法  r.ParseForm() if r.Method == "GET"{ // html := `<html> <head> <title></title> </head> <body> <form action="http://localhost:9090/login" method="post"> username: <input type="text" name="username"> password: <input type="text" name="password"> <input type="submit" value="login"> </form> </body> </html>` t := template.Must(template.New("test").Parse(html)) t.Execute(w, nil) }else{ fmt.Println("username : ", template.HTMLEscapeString(r.Form.Get("username")))//在终端即客户端输出 fmt.Println("password : ", template.HTMLEscapeString(r.Form.Get("password")))//把r.Form.Get("password")转义以后返回字符串 template.HTMLEscape(w, []byte(r.Form.Get("username"))) //在客户端输出,把r.Form.Get("username")转义后写到w  } } func main() { http.HandleFunc("/", index) //设置访问的路由 http.HandleFunc("/login", login) //设置访问的路由 err := http.ListenAndServe(":9090", nil) //设置监听的端口 if err != nil{ log.Fatal("ListenAndServe : ", err) } }

访问http://localhost:9090/

访问http://localhost:9090/login

若是仅传入字符串:

服务端返回:

method POST
username :  hello
password :  allen
map[]
map[]
path /favicon.ico scheme []

客户端:

 

当时若是username输入的是<script>alert()</script>

客户端返回:

可见html/template包默认帮你过滤了html标签

 

func JSEscape

func JSEscape(w io.Writer, b []byte)

函数向w中写入b的JavaScript转义等价表示。

func JSEscapeString

func JSEscapeString(s string) string

返回s的JavaScript转义等价表示字符串。

func JSEscaper

func JSEscaper(args ...interface{}) string

函数返回其全部参数文本表示的JavaScript转义等价表示字符串。

func URLQueryEscaper

func URLQueryEscaper(args ...interface{}) string

函数返回其全部参数文本表示的能够嵌入URL查询的转义等价表示字符串。

相关文章
相关标签/搜索