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查詢的轉義等價表示字符串。

相關文章
相關標籤/搜索