參考: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
有兩個經常使用的傳入參數的類型:函數
type Template struct { *parse.Tree // 內含隱藏或非導出字段 }
表明一個解析好的模板,*parse.Tree字段僅僅是暴露給html/template包使用的,所以其餘人應該視字段未導出。post
func New(name string) *Template
建立一個名爲name的模板。ui
func (t *Template) Parse(text string) (*Template, error)
Parse方法將字符串text解析爲模板。嵌套定義的模板會關聯到最頂層的t。Parse能夠屢次調用,但只有第一次調用能夠包含空格、註釋和模板定義以外的文本。若是後面的調用在解析後仍剩餘文本會引起錯誤、返回nil且丟棄剩餘文本;若是解析獲得的模板已有相關聯的同名模板,會覆蓋掉原模板。
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) } }
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) } }
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 (t *Template) Funcs(funcMap FuncMap) *Template
Funcs方法向模板t的函數字典里加入參數funcMap內的鍵值對。若是funcMap某個鍵值對的值不是函數類型或者返回值不符合要求會panic。可是,能夠對t函數列表的成員進行重寫。方法返回t以便進行鏈式調用。
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,模板執行會停止並將該錯誤返回給執行模板的調用者。
下面是一個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(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
注意:
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 (t *Template) New(name string) *Template
New方法建立一個和t關聯的名字爲name的模板並返回它。這種能夠傳遞的關聯容許一個模板使用template action調用另外一個模板。
func (t *Template) Lookup(name string) *Template
Lookup方法返回與t關聯的名爲name的模板,若是沒有這個模板返回nil。
func (t *Template) Templates() []*Template
Templates方法返回與t相關聯的模板的切片,包括t本身。
當一個Template中有多個模版時,你須要指定解析的模版,所以在這裏使用的是ExecuteTemplate,而不是Execute
func (t *Template) Name() string
返回模板t的名字。
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 (t *Template) ParseFiles(filenames ...string) (*Template, error)
ParseFiles函數建立一個模板並解析filenames指定的文件裏的模板定義。返回的模板的名字是第一個文件的文件名(不含擴展名),內容爲解析後的第一個文件的內容。至少要提供一個文件。若是發生錯誤,會中止解析並返回nil。
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 (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 (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(w io.Writer, b []byte)
函數向w中寫入b的HTML轉義等價表示。
func HTMLEscapeString(s string) string
返回s的HTML轉義等價表示字符串。
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(w io.Writer, b []byte)
函數向w中寫入b的JavaScript轉義等價表示。
func JSEscapeString(s string) string
返回s的JavaScript轉義等價表示字符串。
func JSEscaper(args ...interface{}) string
函數返回其全部參數文本表示的JavaScript轉義等價表示字符串。
func URLQueryEscaper(args ...interface{}) string
函數返回其全部參數文本表示的能夠嵌入URL查詢的轉義等價表示字符串。