亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go模板template用法詳解

 更新時(shí)間:2022年04月18日 10:15:14   作者:駿馬金龍  
這篇文章主要介紹了Go標(biāo)準(zhǔn)庫(kù)template模板用法詳解;包括GO模板注釋,作用域,語法,函數(shù)等知識(shí),需要的朋友可以參考下

本文只介紹template的語法和用法,關(guān)于template包的函數(shù)、方法、template的結(jié)構(gòu)和原理,見:深入解析Go template模板使用詳解。

入門示例

以下為test.html文件的內(nèi)容,里面使用了一個(gè)template語法{{.}}。

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>Go Web</title>
	</head>
	<body>
		{{ . }}
	</body>
</html>

以下是test.html同目錄下的一個(gè)go web程序:

package main

import (
	"html/template"
	"net/http"
)

func tmpl(w http.ResponseWriter, r *http.Request) {
	t1, err := template.ParseFiles("test.html")
	if err != nil {
		panic(err)
	}
	t1.Execute(w, "hello world")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/tmpl", tmpl)
	server.ListenAndServe()
}

前面的html文件中使用了一個(gè)template的語法{{.}},這部分是需要通過go的template引擎進(jìn)行解析,然后替換成對(duì)應(yīng)的內(nèi)容。

在go程序中,handler函數(shù)中使用template.ParseFiles("test.html"),它會(huì)自動(dòng)創(chuàng)建一個(gè)模板(關(guān)聯(lián)到變量t1上),并解析一個(gè)或多個(gè)文本文件(不僅僅是html文件),解析之后就可以使用Execute(w,"hello world")去執(zhí)行解析后的模板對(duì)象,執(zhí)行過程是合并、替換的過程。例如上面的{{.}}中的.會(huì)替換成當(dāng)前對(duì)象"hello world",并和其它純字符串內(nèi)容進(jìn)行合并,最后寫入w中,也就是發(fā)送到瀏覽器"hello world"。

本文不解釋這些template包的函數(shù)、方法以及更底層的理論知識(shí),本文只解釋template的語法,如果覺得這些無法理解,或者看不懂官方手冊(cè),請(qǐng)看深入解析Go template模板使用詳解

關(guān)于點(diǎn)"."和作用域

在寫template的時(shí)候,會(huì)經(jīng)常用到"."。比如{{.}}、{{len .}}、{{.Name}}{{$x.Name}}等等。

在template中,點(diǎn)"."代表當(dāng)前作用域的當(dāng)前對(duì)象。它類似于java/c++的this關(guān)鍵字,類似于perl/python的self。如果了解perl,它更可以簡(jiǎn)單地理解為默認(rèn)變量$_。

例如,前面示例test.html中{{.}},這個(gè)點(diǎn)是頂級(jí)作用域范圍內(nèi)的,它代表Execute(w,"hello worold")的第二個(gè)參數(shù)"hello world"。也就是說它代表這個(gè)字符串對(duì)象。

再例如,有一個(gè)Person struct。

type Person struct {
	Name string
	Age  int
}

func main(){
	p := Person{"longshuai",23}
	tmpl, _ := template.New("test").Parse("Name: {{.Name}}, Age: {{.Age}}")
	_ = tmpl.Execute(os.Stdout, p)
}

這里{{.Name}}{{.Age}}中的點(diǎn)"."代表的是頂級(jí)作用域的對(duì)象p,所以Execute()方法執(zhí)行的時(shí)候,會(huì)將{{.Name}}替換成p.Name,同理{{.Age}}替換成{{p.Age}}

但是并非只有一個(gè)頂級(jí)作用域,range、with、if等內(nèi)置action都有自己的本地作用域。它們的用法后文解釋,這里僅引入它們的作用域來解釋"."。

例如下面的例子,如果看不懂也沒關(guān)系,只要從中理解"."即可。

package main

import (
	"os"
	"text/template"
)

type Friend struct {
	Fname string
}
type Person struct {
	UserName string
	Emails   []string
	Friends  []*Friend
}

func main() {
	f1 := Friend{Fname: "xiaofang"}
	f2 := Friend{Fname: "wugui"}
	t := template.New("test")
	t = template.Must(t.Parse(
`hello {{.UserName}}!
{{ range .Emails }}
an email {{ . }}
{{- end }}
{{ with .Friends }}
{{- range . }}
my friend name is {{.Fname}}
{{- end }}
{{ end }}`))
	p := Person{UserName: "longshuai",
		Emails:  []string{"a1@qq.com", "a2@gmail.com"},
		Friends: []*Friend{&f1, &f2}}
	t.Execute(os.Stdout, p)
}

輸出結(jié)果:

hello longshuai!

an email a1@qq.com
an email a2@gmail.com

my friend name is xiaofang
my friend name is wugui

這里定義了一個(gè)Person結(jié)構(gòu),它有兩個(gè)slice結(jié)構(gòu)的字段。在Parse()方法中:

  • 頂級(jí)作用域的{{.UserName}}、{{.Emails}}{{.Friends}}中的點(diǎn)都代表Execute()的第二個(gè)參數(shù),也就是Person對(duì)象p,它們?cè)趫?zhí)行的時(shí)候會(huì)分別被替換成p.UserName、p.Emails、p.Friends。
  • 因?yàn)镋mails和Friend字段都是可迭代的,在{{range .Emails}}...{{end}}這一段結(jié)構(gòu)內(nèi)部an email {{.}},這個(gè)"."代表的是range迭代時(shí)的每個(gè)元素對(duì)象,也就是p.Emails這個(gè)slice中的每個(gè)元素。
  • 同理,with結(jié)構(gòu)內(nèi)部{{range .}}的"."代表的是p.Friends,也就是各個(gè),再此range中又有一層迭代,此內(nèi)層{{.Fname}}的點(diǎn)代表Friend結(jié)構(gòu)的實(shí)例,分別是&f1&f2,所以{{.Fname}}代表實(shí)例對(duì)象的Fname字段。

去除空白

template引擎在進(jìn)行替換的時(shí)候,是完全按照文本格式進(jìn)行替換的。除了需要評(píng)估和替換的地方,所有的行分隔符、空格等等空白都原樣保留。所以,對(duì)于要解析的內(nèi)容,不要隨意縮進(jìn)、隨意換行。

可以在{{符號(hào)的后面加上短橫線并保留一個(gè)或多個(gè)空格"- "來去除它前面的空白(包括換行符、制表符、空格等),即{{- xxxx。

}}的前面加上一個(gè)或多個(gè)空格以及一個(gè)短橫線"-"來去除它后面的空白,即xxxx -}}。

例如:

{{23}} < {{45}}        -> 23 < 45
{{23}} < {{- 45}}      ->  23 <45
{{23 -}} < {{45}}      ->  23< 45
{{23 -}} < {{- 45}}    ->  23<45

其中{{23 -}}中的短橫線去除了這個(gè)替換結(jié)構(gòu)后面的空格,即}} <中間的空白。同理{{- 45}}的短橫線去除了< {{中間的空白。

再看上一節(jié)的例子中:

t.Parse(
`hello {{.UserName}}!
{{ range .Emails }}
an email {{ . }}
{{- end }}
{{ with .Friends }}
{{- range . }}
my friend name is {{.Fname}}
{{- end }}
{{ end }}`)

注意,上面沒有進(jìn)行縮進(jìn)。因?yàn)榭s進(jìn)的制表符或空格在替換的時(shí)候會(huì)保留。

第一行和第二行之間輸出時(shí)會(huì)換行輸出,不僅如此,range {{.Emails}}自身也占一行,在替換的時(shí)候它會(huì)被保留為空行。除非range前面沒加{{-。由于range的{{- end加上了去除前綴空白,所以每次迭代的時(shí)候,每個(gè)元素之間都換行輸出但卻不多一空行,如果這里的end去掉{{-,則每個(gè)迭代的元素之間輸出的時(shí)候都會(huì)有空行。同理后面的with和range。

注釋

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

注釋后的內(nèi)容不會(huì)被引擎進(jìn)行替換。但需要注意,注釋行在替換的時(shí)候也會(huì)占用行,所以應(yīng)該去除前綴和后綴空白,否則會(huì)多一空行。

{{- /* a comment without prefix/suffix space */}}
{{/* a comment without prefix/suffix space */ -}}
{{- /* a comment without prefix/suffix space */ -}}

注意,應(yīng)該只去除前綴或后綴空白,不要同時(shí)都去除,否則會(huì)破壞原有的格式。例如:

t.Parse(
`hello {{.UserName}}!
{{- /* this line is a comment */}}
{{ range .Emails }}
an email {{ . }}
{{- end }}

管道pipeline

pipeline是指產(chǎn)生數(shù)據(jù)的操作。比如{{.}}、{{.Name}}、funcname args等。

可以使用管道符號(hào)|鏈接多個(gè)命令,用法和unix下的管道類似:|前面的命令將運(yùn)算結(jié)果(或返回值)傳遞給后一個(gè)命令的最后一個(gè)位置。

例如:

{{.}} | printf "%s\n" "abcd"

{{.}}的結(jié)果將傳遞給printf,且傳遞的參數(shù)位置是"abcd"之后。

命令可以有超過1個(gè)的返回值,這時(shí)第二個(gè)返回值必須為err類型。

需要注意的是,并非只有使用了|才是pipeline。Go template中,pipeline的概念是傳遞數(shù)據(jù),只要能產(chǎn)生數(shù)據(jù)的,都是pipeline。這使得某些操作可以作為另一些操作內(nèi)部的表達(dá)式先運(yùn)行得到結(jié)果,就像是Unix下的命令替換一樣。

例如,下面的(len "output")是pipeline,它整體先運(yùn)行。

{{println (len "output")}}

下面是Pipeline的幾種示例,它們都輸出"output"

{{`"output"`}}
{{printf "%q" "output"}}
{{"output" | printf "%q"}}
{{printf "%q" (print "out" "put")}}
{{"put" | printf "%s%s" "out" | printf "%q"}}
{{"output" | printf "%s" | printf "%q"}}

變量

可以在template中定義變量:

// 未定義過的變量
$var := pipeline

// 已定義過的變量
$var = pipeline

例如:

{{- $how_long :=(len "output")}}
{{- println $how_long}}   // 輸出6

再例如:

tx := template.Must(template.New("hh").Parse(
`{{range $x := . -}}
{{$y := 333}}
{{- if (gt $x 33)}}{{println $x $y ($z := 444)}}{{- end}}
{{- end}}
`))
s := []int{11, 22, 33, 44, 55}
_ = tx.Execute(os.Stdout, s)

輸出結(jié)果:

44 333 444
55 333 444

上面的示例中,使用range迭代slice,每個(gè)元素都被賦值給變量$x,每次迭代過程中,都新設(shè)置一個(gè)變量$y,在內(nèi)層嵌套的if結(jié)構(gòu)中,可以使用這個(gè)兩個(gè)外層的變量。在if的條件表達(dá)式中,使用了一個(gè)內(nèi)置的比較函數(shù)gt,如果$x大于33,則為true。在println的參數(shù)中還定義了一個(gè)$z,之所以能定義,是因?yàn)?code>($z := 444)的過程是一個(gè)Pipeline,可以先運(yùn)行。

需要注意三點(diǎn):

  • 變量有作用域,只要出現(xiàn)end,則當(dāng)前層次的作用域結(jié)束。內(nèi)層可以訪問外層變量,但外層不能訪問內(nèi)層變量。
  • 有一個(gè)特殊變量$,它代表模板的最頂級(jí)作用域?qū)ο?通俗地理解,是以模板為全局作用域的全局變量),在Execute()執(zhí)行的時(shí)候進(jìn)行賦值,且一直不變。例如上面的示例中,$ = [11 22 33 44 55]。再例如,define定義了一個(gè)模板t1,則t1中的$作用域只屬于這個(gè)t1。
  • 變量不可在模板之間繼承。普通變量可能比較容易理解,但對(duì)于特殊變量"."和"$",比較容易搞混。見下面的例子。

例如:

func main() {
	t1 := template.New("test1")
	tmpl, _ := t1.Parse(
`
{{- define "T1"}}ONE {{println .}}{{end}}
{{- define "T2"}}{{template "T1" $}}{{end}}
{{- template "T2" . -}}
`)
	_ = tmpl.Execute(os.Stdout, "hello world")
}

上面使用define額外定義了T1和T2兩個(gè)模板,T2中嵌套了T1。{{template "T2" .}}的點(diǎn)代表頂級(jí)作用域的"hello world"對(duì)象。在T2中使用了特殊變量$,這個(gè)$的范圍是T2的,不會(huì)繼承頂級(jí)作用域"hello world"。但因?yàn)閳?zhí)行T2的時(shí)候,傳遞的是".",所以這里的$的值仍然是"hello world"。

不僅$不會(huì)在模板之間繼承,.也不會(huì)在模板之間繼承(其它所有變量都不會(huì)繼承)。實(shí)際上,template可以看作是一個(gè)函數(shù),它的執(zhí)行過程是template("T2",.)。如果把上面的$換成".",結(jié)果是一樣的。如果換成{{template "T2"}},則$=nil

如果看不懂這些,后文有解釋。

條件判斷

有以下幾種if條件判斷語句,其中第三和第四是等價(jià)的。

{{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}}

需要注意的是,pipeline為false的情況是各種數(shù)據(jù)對(duì)象的0值:數(shù)值0,指針或接口是nil,數(shù)組、slice、map或string則是len為0。

range...end迭代

有兩種迭代表達(dá)式類型:

{{range pipeline}} T1 {{end}}
{{range pipeline}} T1 {{else}} T0 {{end}}

range可以迭代slice、數(shù)組、map或channel。迭代的時(shí)候,會(huì)設(shè)置"."為當(dāng)前正在迭代的元素。

對(duì)于第一個(gè)表達(dá)式,當(dāng)?shù)鷮?duì)象的值為0值時(shí),則range直接跳過,就像if一樣。對(duì)于第二個(gè)表達(dá)式,則在迭代到0值時(shí)執(zhí)行else語句。

tx := template.Must(template.New("hh").Parse(
`{{range $x := . -}}
{{println $x}}
{{- end}}
`))
s := []int{11, 22, 33, 44, 55}
_ = tx.Execute(os.Stdout, s)

需注意的是,range的參數(shù)部分是pipeline,所以在迭代的過程中是可以進(jìn)行賦值的。但有兩種賦值情況:

{{range $value := .}}
{{range $key,$value := .}}

如果range中只賦值給一個(gè)變量,則這個(gè)變量是當(dāng)前正在迭代元素的值。如果賦值給兩個(gè)變量,則第一個(gè)變量是索引值(map/slice是數(shù)值,map是key),第二個(gè)變量是當(dāng)前正在迭代元素的值。

下面是在html中使用range的一個(gè)示例。test.html文件內(nèi)容如下:

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>Go Web</title>
	</head>
	<body>
		<ul>
			{{ range . }}
				<li>{{ . }}</li>
			{{ else }}
				<li> Nothing to show </li>
			{{ end}}
		</ul>
	</body>
</html>

以下是test.html同目錄下的go程序文件:

package main

import (
	"html/template"
	"net/http"
)

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/process", process)
	server.ListenAndServe()
}

func process(w http.ResponseWriter, r *http.Request) {
	t1 := template.Must(template.ParseFiles("test.html"))
	s := []string{
		"星期一",
		"星期二",
		"星期三",
		"星期四",
		"星期五",
		"星期六",
		"星期日",}
	t1.Execute(w, s)
}

with...end

with用來設(shè)置"."的值。兩種格式:

{{with pipeline}} T1 {{end}}
{{with pipeline}} T1 {{else}} T0 {{end}}

對(duì)于第一種格式,當(dāng)pipeline不為0值的時(shí)候,點(diǎn)"."設(shè)置為pipeline運(yùn)算的值,否則跳過。對(duì)于第二種格式,當(dāng)pipeline為0值時(shí),執(zhí)行else語句塊,否則"."設(shè)置為pipeline運(yùn)算的值,并執(zhí)行T1。

例如:

{{with "xx"}}{{println .}}{{end}}

上面將輸出xx,因?yàn)?quot;."已經(jīng)設(shè)置為"xx"。

內(nèi)置函數(shù)和自定義函數(shù)

template定義了一些內(nèi)置函數(shù),也支持自定義函數(shù)。關(guān)于如何自定義函數(shù),見深入解析Go template模板使用詳解。

以下是內(nèi)置的函數(shù)列表:

and
    返回第一個(gè)為空的參數(shù)或最后一個(gè)參數(shù)??梢杂腥我舛鄠€(gè)參數(shù)。
    and x y等價(jià)于if x then y else x

not
    布爾取反。只能一個(gè)參數(shù)。

or
    返回第一個(gè)不為空的參數(shù)或最后一個(gè)參數(shù)??梢杂腥我舛鄠€(gè)參數(shù)。
    "or x y"等價(jià)于"if x then x else y"。

print
printf
println
    分別等價(jià)于fmt包中的Sprint、Sprintf、Sprintln

len
    返回參數(shù)的length。

index
    對(duì)可索引對(duì)象進(jìn)行索引取值。第一個(gè)參數(shù)是索引對(duì)象,后面的參數(shù)是索引位。
    "index x 1 2 3"代表的是x[1][2][3]。
    可索引對(duì)象包括map、slice、array。

call
    顯式調(diào)用函數(shù)。第一個(gè)參數(shù)必須是函數(shù)類型,且不是template中的函數(shù),而是外部函數(shù)。
    例如一個(gè)struct中的某個(gè)字段是func類型的。
    "call .X.Y 1 2"表示調(diào)用dot.X.Y(1, 2),Y必須是func類型,函數(shù)參數(shù)是1和2。
    函數(shù)必須只能有一個(gè)或2個(gè)返回值,如果有第二個(gè)返回值,則必須為error類型。

除此之外,還內(nèi)置一些用于比較的函數(shù):

eq arg1 arg2:
    arg1 == arg2時(shí)為true
ne arg1 arg2:
    arg1 != arg2時(shí)為true
lt arg1 arg2:
    arg1 < arg2時(shí)為true
le arg1 arg2:
    arg1 <= arg2時(shí)為true
gt arg1 arg2:
    arg1 > arg2時(shí)為true
ge arg1 arg2:
    arg1 >= arg2時(shí)為true

對(duì)于eq函數(shù),支持多個(gè)參數(shù):

eq arg1 arg2 arg3 arg4...

它們都和第一個(gè)參數(shù)arg1進(jìn)行比較。它等價(jià)于:

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

示例:

{{ if (gt $x 33) }}{{println $x}}{{ end }}

嵌套template:define和template

define可以直接在待解析內(nèi)容中定義一個(gè)模板,這個(gè)模板會(huì)加入到common結(jié)構(gòu)組中,并關(guān)聯(lián)到關(guān)聯(lián)名稱上。如果不理解,還是建議閱讀深入解析Go template模板使用詳解。

定義了模板之后,可以使用template這個(gè)action來執(zhí)行模板。template有兩種格式:

{{template "name"}}
{{template "name" pipeline}}

第一種是直接執(zhí)行名為name的template,點(diǎn)設(shè)置為nil。第二種是點(diǎn)"."設(shè)置為pipeline的值,并執(zhí)行名為name的template??梢詫emplate看作是函數(shù):

template("name)
template("name",pipeline)

例如:

func main() {
	t1 := template.New("test1")
	tmpl, _ := t1.Parse(
`{{- define "T1"}}ONE {{println .}}{{end}}
{{- define "T2"}}TWO {{println .}}{{end}}
{{- define "T3"}}{{template "T1"}}{{template "T2" "haha"}}{{end}}
{{- template "T3" -}}
`)
	_ = tmpl.Execute(os.Stdout, "hello world")
}

輸出結(jié)果:

ONE <nil>
TWO haha

上面定義了4個(gè)模板,一個(gè)是test1,另外三個(gè)是使用define來定義的T1、T2、T3,其中t1是test1模板的關(guān)聯(lián)名稱。T1、T2、T3和test1共享一個(gè)common結(jié)構(gòu)。其中T3中包含了執(zhí)行T1和T2的語句。最后只要{{template T3}}就可以執(zhí)行T3,執(zhí)行T3又會(huì)執(zhí)行T1和T2。也就是實(shí)現(xiàn)了嵌套。此外,執(zhí)行{{template "T1"}}時(shí),點(diǎn)設(shè)置為nil,而{{temlate "T2" "haha"}}的點(diǎn)設(shè)置為了"haha"。

注意,模板之間的變量是不會(huì)繼承的。

下面是html文件中嵌套模板的幾個(gè)示例。

t1.html文件內(nèi)容如下:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=9">
	<title>Go Web Programming</title>
</head>

<body>
	<div> This is t1.html before</div>
	<div>This is the value of the dot in t1.html - [{{ . }}]</div>
	<hr />
	{{ template "t2.html" }}
	<hr />
	<div> This is t1.html after</div>
</body>

</html>

因?yàn)閮?nèi)部有{{template "t2.html"}},且此處沒有使用define去定義名為"t2.html"的模板,所以需要加載解析名為t2.html的文件。t2.html文件內(nèi)容如下:

<div style="background-color: yellow;">
	This is t2.html<br/>
	This is the value of the dot in t2.html - [{{ . }}]
</div>

處理這兩個(gè)文件的handler函數(shù)如下:

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("t1.html", "t2.html")
	t.Execute(w, "Hello World!")
}

上面也可以不額外定義t2.html文件,而是直接在t1.html文件中使用define定義一個(gè)模板。修改t1.html文件如下:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=9">
	<title>Go Web Programming</title>
</head>

<body>
	<div> This is t1.html before</div>
	<div>This is the value of the dot in t1.html - [{{ . }}]</div>
	<hr />
	{{ template "t2.html" }}
	<hr />
	<div> This is t1.html after</div>
</body>

</html>

{{define "t2.html"}}
<div style="background-color: yellow;">
	This is t2.html<br/>
	This is the value of the dot in t2.html - [{{ . }}]
</div>
{{end}}

然后在handler中,只需解析t1.html一個(gè)文件即可。

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("t1.html")
	t.Execute(w, "Hello World!")
}

block塊

{{block "name" pipeline}} T1 {{end}}
	A block is shorthand for defining a template
		{{define "name"}} T1 {{end}}
	and then executing it in place
		{{template "name" pipeline}}
	The typical use is to define a set of root templates that are
	then customized by redefining the block templates within.

根據(jù)官方文檔的解釋:block等價(jià)于define定義一個(gè)名為name的模板,并在"有需要"的地方執(zhí)行這個(gè)模板,執(zhí)行時(shí)將"."設(shè)置為pipeline的值。

但應(yīng)該注意,block的第一個(gè)動(dòng)作是執(zhí)行名為name的模板,如果不存在,則在此處自動(dòng)定義這個(gè)模板,并執(zhí)行這個(gè)臨時(shí)定義的模板。換句話說,block可以認(rèn)為是設(shè)置一個(gè)默認(rèn)模板

例如:

{{block "T1" .}} one {{end}}

它首先表示{{template "T1" .}},也就是說先找到T1模板,如果T1存在,則執(zhí)行找到的T1,如果沒找到T1,則臨時(shí)定義一個(gè){{define "T1"}} one {{end}},并執(zhí)行它。

下面是正常情況下不使用block的示例。

home.html文件內(nèi)容如下:

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>Go Web Programming</title>
	</head>
	<body>
		{{ template "content" }}
	</body>
</html>

在此文件中指定了要執(zhí)行一個(gè)名為"content"的模板,但此文件中沒有使用define定義該模板,所以需要在其它文件中定義名為content的模板?,F(xiàn)在分別在兩個(gè)文件中定義兩個(gè)content模板:

red.html文件內(nèi)容如下:

{{ define "content" }}
	<h1 style="color: red;">Hello World!</h1>
{{ end }}

blue.html文件內(nèi)容如下:

{{ define "content" }}
	<h1 style="color: blue;">Hello World!</h1>
{{ end }}

在handler中,除了解析home.html,還根據(jù)需要解析red.html或blue.html:

func process(w http.ResponseWriter, r *http.Request) {
	rand.Seed(time.Now().Unix())
	t := template.New("test")
	if rand.Intn(10) > 5 {
		t, _ = template.ParseFiles("home.html", "red.html")
	} else {
		t, _ = template.ParseFiles("home.html", "blue.html")
	}
	t.Execute(w,"")
}

如果使用block,那么可以設(shè)置默認(rèn)的content模板。例如將原本定義在blue.html中的content設(shè)置為默認(rèn)模板。

修改home.html:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Go Web Programming</title>
    </head>
    <body>
        {{ block "content" . }}
            <h1 style="color: blue;">Hello World!</h1>
        {{ end }}
    </body>
</html>

然后修改handler:

func process(w http.ResponseWriter, r *http.Request) {
	rand.Seed(time.Now().Unix())
	t := template.New("test")
	if rand.Intn(10) > 5 {
		t, _ = template.ParseFiles("home.html", "red.html")
	} else {
		t, _ = template.ParseFiles("home.html")
	}
	t.Execute(w,"")
}

當(dāng)執(zhí)行else語句塊的時(shí)候,發(fā)現(xiàn)home.html中要執(zhí)行名為content的模板,但在ParseFiles()中并沒有解析包含content模板的文件。于是執(zhí)行block定義的content模板。而執(zhí)行非else語句的時(shí)候,因?yàn)閞ed.html中定義了content,會(huì)直接執(zhí)行red.html中的content。

block通常設(shè)置在頂級(jí)的根文件中,例如上面的home.html中。

html/template的上下文感知

對(duì)于html/template包,有一個(gè)很好用的功能:上下文感知。text/template沒有該功能。

上下文感知具體指的是根據(jù)所處環(huán)境css、js、html、url的path、url的query,自動(dòng)進(jìn)行不同格式的轉(zhuǎn)義。

例如,一個(gè)handler函數(shù)的代碼如下:

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("test.html")
	content := `I asked: <i>"What's up?"</i>`
	t.Execute(w, content)
}

上面content是Execute的第二個(gè)參數(shù),它的內(nèi)容是包含了特殊符號(hào)的字符串。

下面是test.html文件的內(nèi)容:

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>Go Web Programming</title>
	</head>
	<body>
		<div>{{ . }}</div>
		<div><a href="/{{ . }}" rel="external nofollow" >Path</a></div>
		<div><a href="/?q={{ . }}" rel="external nofollow" >Query</a></div>
		<div><a onclick="f('{{ . }}')">Onclick</a></div>
	</body>
</html>

上面test.html中有4個(gè)不同的環(huán)境,分別是html環(huán)境、url的path環(huán)境、url的query環(huán)境以及js環(huán)境。雖然對(duì)象都是{{.}},但解析執(zhí)行后的值是不一樣的。如果使用curl獲取源代碼,結(jié)果將如下:

<html>

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Go Web Programming</title>
</head>

<body>
	<div>I asked: &lt;i&gt;&#34;What&#39;s up?&#34;&lt;/i&gt;</div>
	<div>
		<a href="/I%20asked:%20%3ci%3e%22What%27s%20up?%22%3c/i%3e" rel="external nofollow" >
			Path
		</a>
	</div>
	<div>
		<a href="/?q=I%20asked%3a%20%3ci%3e%22What%27s%20up%3f%22%3c%2fi%3e" rel="external nofollow" >
			Query
		</a>
	</div>
	<div>
		<a onclick="f('I asked: \x3ci\x3e\x22What\x27s up?\x22\x3c\/i\x3e')">
			Onclick
		</a>
	</div>
</body>

</html>

不轉(zhuǎn)義

上下文感知的自動(dòng)轉(zhuǎn)義能讓程序更加安全,比如防止XSS攻擊(例如在表單中輸入帶有<script>...</script>的內(nèi)容并提交,會(huì)使得用戶提交的這部分script被執(zhí)行)。

如果確實(shí)不想轉(zhuǎn)義,可以進(jìn)行類型轉(zhuǎn)換。

type CSS
type HTML
type JS
type URL

轉(zhuǎn)換成指定個(gè)時(shí)候,字符都將是字面意義。

例如:

func process(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("tmpl.html")
	t.Execute(w, template.HTML(r.FormValue("comment")))
}

更多關(guān)于Go模板template用法詳解請(qǐng)查看下面的相關(guān)鏈接

相關(guān)文章

  • Go語言基礎(chǔ)for循環(huán)語句的用法及示例詳解

    Go語言基礎(chǔ)for循環(huán)語句的用法及示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)for循環(huán)語句的用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11
  • GoLang函數(shù)與面向接口編程全面分析講解

    GoLang函數(shù)與面向接口編程全面分析講解

    這篇文章主要介紹了GoLang函數(shù)與面向接口編程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-01-01
  • Golang如何將日志以Json格式輸出到Kafka

    Golang如何將日志以Json格式輸出到Kafka

    這篇文章主要介紹了Golang將日志以Json格式輸出到Kafka的方法,這篇文章還會(huì)提供一種輸出Json格式日志的方法,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • Go語言中如何實(shí)現(xiàn)并發(fā)

    Go語言中如何實(shí)現(xiàn)并發(fā)

    Go的并發(fā)機(jī)制通過協(xié)程和通道的簡(jiǎn)單性和高效性,使得編寫并發(fā)代碼變得相對(duì)容易,這種并發(fā)模型被廣泛用于構(gòu)建高性能的網(wǎng)絡(luò)服務(wù)、并行處理任務(wù)和其他需要有效利用多核處理器的應(yīng)用程序,這篇文章主要介紹了在Go中如何實(shí)現(xiàn)并發(fā),需要的朋友可以參考下
    2023-09-09
  • GoLang逃逸分析講解

    GoLang逃逸分析講解

    我們都知道go語言中內(nèi)存管理工作都是由Go在底層完成的,這樣我們可以不用過多的關(guān)注底層的內(nèi)存問題。本文主要總結(jié)一下?Golang內(nèi)存逃逸分析,需要的朋友可以參考以下內(nèi)容,希望對(duì)大家有幫助
    2022-12-12
  • Go實(shí)現(xiàn)整合Logrus實(shí)現(xiàn)日志打印

    Go實(shí)現(xiàn)整合Logrus實(shí)現(xiàn)日志打印

    這篇文章主要介紹了Go實(shí)現(xiàn)整合Logrus實(shí)現(xiàn)日志打印,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • Go語言實(shí)現(xiàn)關(guān)閉http請(qǐng)求的方式總結(jié)

    Go語言實(shí)現(xiàn)關(guān)閉http請(qǐng)求的方式總結(jié)

    面試的時(shí)候問到如何關(guān)閉http請(qǐng)求,一般人脫口而出的是關(guān)閉response.body,這是錯(cuò)誤的。本文為大家整理了三個(gè)正確關(guān)閉http請(qǐng)求的方法,希望對(duì)大家有所幫助
    2023-02-02
  • golang 占位符和fmt常見輸出介紹

    golang 占位符和fmt常見輸出介紹

    這篇文章主要介紹了golang 占位符和fmt常見輸出介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang分布式應(yīng)用之Redis示例詳解

    Golang分布式應(yīng)用之Redis示例詳解

    這篇文章主要為大家介紹了Golang分布式應(yīng)用之Redis示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • GO語言開發(fā)環(huán)境搭建過程圖文詳解

    GO語言開發(fā)環(huán)境搭建過程圖文詳解

    這篇文章主要介紹了GO語言開發(fā)環(huán)境搭建過程圖文詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01

最新評(píng)論