如何在 Go 中使用模板

介绍

你需要在格式好的输出、文本报告或 HTML 页面中呈现一些数据吗?你可以使用 Go 模板来呈现一些数据吗?任何 Go 程序都可以使用 text/templatehtml/template 包(两者都包含在 Go 标准库中)来优雅地呈现数据。

两种套件都允许您编写文本模板,并将数据传输到它们中,以便将文档格式化为您的喜好。在模板中,您可以循环数据并使用条件逻辑来决定将哪些项目包含在文档中,以及它们应该如何显示。本教程将向您展示如何使用两种模板包。 首先,您将使用文本/模板将一些数据转化为简单文本报告,使用循环、条件逻辑和自定义函数。

前提条件

在开始本教程之前,您只需要安装 Go. 阅读您的操作系统的正确教程:

您还需要使用Go语言的工作知识,包括 创建结构使用结构方法

让我们开始吧。

步骤 1 – 导入文本/模板

假设您想生成关于您对狗狗的某些数据的简单报告。

 1---
 2Name:  Jujube
 3
 4Sex:   Female (spayed)
 5
 6Age:   10 months
 7
 8Breed: German Shepherd/Pitbull
 9
10---
11Name:  Zephyr
12
13Sex:   Male (intact)
14
15Age:   13 years, 3 months
16
17Breed: German Shepherd/Border Collie

这是您将使用text/template包生成的报告。突出的项目是您的数据,其余部分是来自模板的静态文本。模板可以作为代码中的字符串在您的代码中或在自己的文件中生存。它们包含有条件陈述(即如果/else),流量控制陈述(即循环)和函数调用,所有这些都包含在{{{...}}标记中。

要开始,请导航到您的 Go 工作区(‘go env GOPATH’),并为此项目创建一个新的目录:

1cd `go env GOPATH`
2mkdir pets
3cd pets

使用nano或您最喜欢的文本编辑器,打开一个名为pets.go的新文件,并粘贴到:

1nano pets.go
 1[label pets.go]
 2package main
 3
 4import (
 5    "os"
 6    "text/template"
 7)
 8
 9func main() {
10}

该文件声明自己处于包中,包含一个函数,这意味着它可以使用运行来运行。它导入了文本/模板标准库包,允许您编写和渲染模板,以及我们,将用于打印到终端。

步骤 2 - 创建模板数据

在编写模板之前,让我们创建一些数据,将其传入模板中。在导入陈述和主()之前,定义一个名为宠物的结构,该结构包含宠物的姓名,性别,无论宠物是否被调和/中立(未接触),年龄繁殖

 1[label pets.go]
 2. . .
 3type Pet struct {
 4    Name string
 5    Sex string
 6    Intact bool
 7    Age string
 8    Breed string
 9}
10. . .

现在,在主()函数的体内,创建一个宠物片段,以保持关于两个狗的数据:

 1[label pets.go]
 2. . .
 3func main() {
 4    dogs := []Pet{
 5    	{
 6    		Name:   "Jujube",
 7    		Sex:    "Female",
 8    		Intact: false,
 9    		Age:    "10 months",
10    		Breed:  "German Shepherd/Pitbull",
11    	},
12    	{
13    		Name:   "Zephyr",
14    		Sex:    "Male",
15    		Intact: true,
16    		Age:    "13 years, 3 months",
17    		Breed:  "German Shepherd/Border Collie",
18    	},
19    }
20} // end main

当然,您传输给您的模板的数据可以来自任何地方:您的数据库,第三方API等.对于本教程,最简单的就是将一些样本数据粘贴到您的代码中。

现在让我们看看如何在这些包的术语中渲染(或 execute)一个模板。

步骤 3 – 执行一个 Template

在此步骤中,您将看到如何使用文本/模板从模板生成完成的文档,但直到步骤 4 之前,您实际上不会写有用的模板。

创建一个名为「pets.tmpl」的空文本文件,其中包含一些静态文本:

1[label pets.tmpl]
2Nothing here yet.

保存模板并退出编辑器. 如果您正在使用nano,请按CTRL+X,然后按YENTER来确认您的更改。

虽然执行此模板只会打印还没有,但让我们输入您的数据并执行模板,以证明文本/模板是有效的。

 1[label pets.go]
 2    . . .
 3    var tmplFile = pets.tmpl
 4    tmpl, err := template.New(tmplFile).ParseFiles(tmplFile)
 5    if err != nil {
 6    	panic(err)
 7    }
 8    err = tmpl.Execute(os.Stdout, dogs)
 9    if err != nil {
10    	panic(err)
11    }
12} // end main

在这个代码片段中,你正在使用Template.New来创建一个新的Template,然后在结果的模板上调用ParseFiles来解析你的最小模板文件. 检查错误后,你正在调用新模板的执行方法,通过os.Stdout来打印到终端的完成报告,并通过你的片段。 对于第一个论点,你可以通过任何实现 io.Writer接口的内容,这意味着你可以将报告写到一个文件中,例如。 我们将看到如何在以后进行。

整个计划应该是这样的:

 1package main
 2
 3import (
 4    "os"
 5    "text/template"
 6)
 7
 8type Pet struct {
 9    Name string
10    Sex string
11    Intact bool
12    Age string
13    Breed string
14}
15
16func main() {
17    dogs := []Pet{
18    	{
19    		Name:   "Jujube",
20    		Sex:    "Female",
21    		Intact: false,
22    		Age:    "10 months",
23    		Breed:  "German Shepherd/Pitbull",
24    	},
25    	{
26    		Name:   "Zephyr",
27    		Sex:    "Male",
28    		Intact: true,
29    		Age:    "13 years, 3 months",
30    		Breed:  "German Shepherd/Border Collie",
31    	},
32    }
33    var tmplFile = pets.tmpl
34    tmpl, err := template.New(tmplFile).ParseFiles(tmplFile)
35    if err != nil {
36    	panic(err)
37    }
38    err = tmpl.Execute(os.Stdout, dogs)
39    if err != nil {
40    	panic(err)
41    }
42} // end main

保存程序,然后用go run运行它:

1go run pets.go
1[secondary_label Output]
2Nothing here yet.

该程序尚未打印您的数据,但至少代码运行干净。

步骤 4 - 写一个寺庙

是的,它包含了一些静态文本,将出现在最终输出中不变,但它还包含了 actions,这是模板引擎的指示,告诉它如何通过传入的数据,以及要包括什么在输出中。

传输到模板中的数据可能完全是任何东西,但它是常见的传输在片段,数组或地图 - 一些可重复的。

走过一片沙子

在 Go 代码中,您可以使用for循环的开头语句中的范围重复在一个片段上。在模板中,您可以使用range操作用于相同的目的,但它有一个不同的语法:没有for,但有一个添加的end来关闭循环。

打开pets.tmpl,并用以下内容取代其内容:

1[label pets.tmpl]
2{{ range . }}
3---
4(Pet will appear here...)
5{{ end }}

范围行动在这里采取一个论点:方针(.),它指的是整个片段. 循环在底部被关闭的{{ end }}

保存「pets.tmpl」並再次執行「pets.go」:

1go run pets.go
1[secondary_label Output]
2
3---
4(Pet will appear here...)
5
6---
7(Pet will appear here...)

静态文本被打印两次,因为你的片段里有两个狗,现在让我们用一些更有用的静态文本,以及狗的数据来代替。

显示一个领域

在此模板中将 . 转换为 范围时,点指的是整个片段,但在 范围`循环的每个迭代中,点指的是片段中的当前项目。

显示一个字段很简单,就像将其包裹在弯曲的子里,并用点子预先打开它,然后用以下内容更换它的内容:

 1[label pets.tmpl]
 2{{ range . }}
 3---
 4Name:  {{ .Name }}
 5
 6Sex:   {{ .Sex }}
 7
 8Age:   {{ .Age }}
 9
10Breed: {{ .Breed }}
11{{ end }}

现在pets.go将为两只狗打印四个五个字段,其中包括一些字段的标签(我们会在一瞬间进入第五个字段)。

保存并重新运行程序:

1go run pets.go
 1[secondary_label Output]
 2
 3---
 4Name:  Jujube
 5
 6Sex:   Female
 7
 8Age:   10 months
 9
10Breed: German Shepherd/Pitbull
11
12---
13Name:  Zephyr
14
15Sex:   Male
16
17Age:   13 years, 3 months
18
19Breed: German Shepherd/Border Collie

现在让我们看看如何使用一些条件逻辑来显示第五个字段。

使用条件

由于我们没有将Intact字段添加到模板中,所以我们没有将Intact字段添加到模板中,这是因为它不会非常友好读者。 想象一下,如果你的兽医在关于你的狗的摘要中说Intact: false的话。 虽然将这个字段存储为布尔语而不是字符串,而Intact是这个字段的良好性别中立的名称,我们可以通过使用 if-else 操作在我们的最终报告中显示它。

再次打开「pets.tmpl」并添加这里所示的突出部分:

 1[label pets.tmpl]
 2{{ range . }}
 3---
 4Name:  {{ .Name }}
 5
 6Sex:   {{ .Sex }} ({{ if .Intact }}intact{{ else }}fixed{{ end }})
 7
 8Age:   {{ .Age }}
 9
10Breed: {{ .Breed }}
11{{ end }}

该模板现在检查未接触字段是否正确,并打印(未接触)如果是这样或(固定)如果不是这样,但我们可以做得更好。

 1[label pets.tmpl]
 2{{ range . }}
 3---
 4Name:  {{ .Name }}
 5
 6Sex:   {{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if (eq .Sex "Female") }}spayed{{ else }}neutered{{ end }}{{ end }})
 7
 8Age:   {{ .Age }}
 9
10Breed: {{ .Breed }}
11{{ end }}

保存模板并运行pets.go:

1go run pets.go
 1[secondary_label Output]
 2
 3---
 4Name:  Jujube
 5
 6Sex:   Female (spayed)
 7
 8Age:   10 months
 9
10Breed: German Shepherd/Pitbull
11
12---
13Name:  Zephyr
14
15Sex:   Male (intact)
16
17Age:   13 years, 3 months
18
19Breed: German Shepherd/Border Collie

我們有兩隻狗,但有三個可能的例子顯示不完整。讓我們在pets.go中添加一隻狗,以覆蓋所有三個例子。

 1[label pets.go]
 2. . .
 3func main() {
 4    dogs := []Pet{
 5    	{
 6    		Name:   "Jujube",
 7    		Sex:    "Female",
 8    		Intact: false,
 9    		Age:    "10 months",
10    		Breed:  "German Shepherd/Pitbull",
11    	},
12    	{
13    		Name:   "Zephyr",
14    		Sex:    "Male",
15    		Intact: true,
16    		Age:    "13 years, 3 months",
17    		Breed:  "German Shepherd/Border Collie",
18    	},
19    	{
20    		Name:	"Bruce Wayne",
21    		Sex:	"Male",
22    		Intact:	false,
23    		Age:	"3 years, 8 months",
24    		Breed:	"Chihuahua",
25    	},
26    }
27. . .

现在保存并运行pets.go:

1go run pets.go
 1[secondary_label Output]
 2
 3---
 4Name:  Jujube
 5
 6Sex:   Female (spayed)
 7
 8Age:   10 months
 9
10Breed: German Shepherd/Pitbull
11
12---
13Name:  Zephyr
14
15Sex:   Male (intact)
16
17Age:   13 years, 3 months
18
19Breed: German Shepherd/Border Collie
20
21---
22Name:  Bruce Wayne
23
24Sex:   Male (neutered)
25
26Age:   3 years, 8 months
27
28Breed: Chihuahua

很棒 - 看起来像预期的那样。

现在,让我们讨论模板函数,例如您刚刚使用的eq函数。

使用 Template 功能

除了「eq」之外,还有其他用于比较字段值并返回布尔值的函数:『gt』(>),『ne』(!=),『le』(<=),等等,您可以用两种方式调用这些函数(以及任何模板函数)(https://pkg.go.dev/text/template#hdr-Functions):

  1. 写函数名,然后是一个或多个参数,每个参数之间有空间。这就是你上面使用的‘eq’的方式:‘eq.Sex `女性``.
  2. 首先写一个参数,然后是管道(‘LINK’),然后是函数名,然后是任何更多的参数。 这类似于 Unix命令行上的命令管道,和命令行一样,你可以连接许多函数呼叫在一个 管道中,将一个呼叫的输出作为输入传递给下一个呼叫,等等。

因此,虽然您的模板中的eq比较被写成eq.Sex 女性,但它也可以被写成.Sexeq女性`。

让我们使用len函数来显示报告的顶部的狗的数量。

1[label pets.tmpl]
2Number of dogs: {{ . | len -}}
3
4{{ range . }}
5. . .

你也可以写``字。

注意在关闭的双弯曲的轴承旁边的轴承(-)。这将防止新闻线(\n)在操作后被打印。您还可以将轴承粘贴到打开双弯曲的轴承({-)上,以便在操作之前抑制新线。

保存模板并运行pets.go:

1go run pets.go
 1[secondary_label Output]
 2Number of dogs: 3
 3---
 4Name:  Jujube
 5
 6Sex:   Female (spayed)
 7
 8Age:   10 months
 9
10Breed: German Shepherd & Pitbull
11
12---
13Name:  Zephyr
14
15Sex:   Male (intact)
16
17Age:   13 years, 3 months
18
19Breed: German Shepherd & Border Collie
20
21---
22Name:  Bruce Wayne
23
24Sex:   Male (neutered)
25
26Age:   3 years, 8 months
27
28Breed: Chihuahua

由于标签中的条纹,在狗的数量标签和第一只狗之间没有空线。

好消息是,你可以让任何 Go 函数可用于你的模板,只要它返回一个值,或者两个值,如果第二个值是错误类型。

在 Templates 中使用 Go 函数

假设你想写一个模板,该模板需要一块狗,只打印最后一块。你可以使用嵌入式slic函数在模板中获得一小部分片,这与Go中的mySlice[x:y]相似。你可以写{{slic. 2 }}来获得三项片的最后一个项目,尽管slic函数返回另一个片,而不是一个项目。

但是,您如何可以引用模板中的最后一个片段的索引? 可用的是len函数,但片段中的最后一个项目的索引为len - 1,不幸的是,您无法在模板中进行算法。

这是一个自定义函数非常有用的地方,让我们写一个函数,以减去整数,并使该函数可用于我们的模板。

但是在我们这样做之前,让我们创建一个新的模板,打开一个新的文件 lastPet.tmpl 并粘贴到以下内容:

 1[label lastPet.tmpl]
 2{{- range (len . | dec | slice . ) }}
 3---
 4Name:  {{ .Name }}
 5
 6Sex:   {{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if ("Female" | eq .Sex) }}spayed{{ else }}neutered{{ end }}{{ end }})
 7
 8Age:   {{ .Age }}
 9
10Breed: {{ .Breed }}
11{{ end -}}

在第一个行上的dec函数呼叫引用了你要定义的自定义函数,并将其传送到模板中。在pets.go中的main()函数中,在dogs片段下方和呼叫前,在tmpl.Execute()进行以下更改(突出):

 1[label pets.go]
 2    . . .
 3    funcMap := template.FuncMap{
 4    	"dec": func(i int) int { return i - 1 },
 5    }
 6    var tmplFile = lastPet.tmpl
 7    tmpl, err := template.New(tmplFile).Funcs(funcMap).ParseFiles(tmplFile)
 8    if err != nil {
 9    	panic(err)
10    }
11    . . .

首先,您声明一个FuncMap,这是一个函数的地图:密钥是函数名称,可供您的模板使用,值是函数本身。你这里的一个函数,即dec,是一个内线提供的 匿名函数,因为它很短。

然后你正在更改你的模板文件名称. 最后,你在调用ParseFile之前插入一个调用到Template.Funcs,传递给你刚刚定义的funcMap

在运行代码之前,让我们了解您的模板中的范围操作发生了什么:

1[label template_last_pet.go]
2{{- range (len . | dec | slice . ) }}

您正在获得您的片段的长度,将其转移到您自定义的dec函数中,以减去一个,然后将其作为第二个参数转移到前面讨论的slice函数中。

保存pets.go并运行它:

1go run pets.go
 1[secondary_label Output]
 2
 3---
 4Name:  Bruce Wayne
 5
 6Sex:   Male (neutered)
 7
 8Age:   3 years, 8 months
 9
10Breed: Chihuahua

看起来很好,如果你想显示最后两只狗而不是最后一只狗呢? 编辑lastPet.tmpl并在管道中添加另一个呼叫dec:

1[label lastPet.tmpl]
2{{- range (len . | dec | dec | slice . ) }}
3. . .

保存文件并再次运行pets.go:

1go run pets.go
 1[secondary_label Output]
 2
 3---
 4Name:  Zephyr
 5
 6Sex:   Male (intact)
 7
 8Age:   13 years, 3 months
 9
10Breed: German Shepherd/Border Collie
11
12---
13Name:  Bruce Wayne
14
15Sex:   Male (neutered)
16
17Age:   3 years, 8 months
18
19Breed: Chihuahua

您可能可以想象如何通过让它采取参数并更改其名称来改善dec函数,以便您可以将其称为minus 2而不是dec.

现在假设你想以不同的方式显示Zephyr等混合品种的狗,用强化沙代替削减。你不需要写自己的函数来做这件事,因为字符串包有一个,你可以从任何包中借用一个函数来使用你的模板。

 1[label pets.go]
 2package main
 3
 4import (
 5    "os"
 6    "strings"
 7    "text/template"
 8)
 9. . .
10func main() {
11    . . .
12    funcMap := template.FuncMap{
13    	"dec":     func(i int) int { return i - 1 },
14    	"replace": strings.ReplaceAll,
15    }
16    . . .
17} // end main

您现在正在导入字符串包并将其ReplaceAll函数添加到您的funcMap中,名称为replace

 1[label lastPet.tmpl]
 2{{- range (len . | dec | dec | slice . ) }}
 3---
 4Name:  {{ .Name }}
 5
 6Sex:   {{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if ("Female" | eq .Sex) }}spayed{{ else }}neutered{{ end }}{{ end }})
 7
 8Age:   {{ .Age }}
 9
10Breed: {{ replace .Breed /  &  }}
11{{ end -}}

保存文件,然后再运行一次:

1go run pets.go
 1[secondary_label Output]
 2
 3---
 4Name:  Zephyr
 5
 6Sex:   Male (intact)
 7
 8Age:   13 years, 3 months
 9
10Breed: German Shepherd & Border Collie
11
12---
13Name:  Bruce Wayne
14
15Sex:   Male (neutered)
16
17Age:   3 years, 8 months
18
19Breed: Chihuahua

Zephyr的品种现在含有砂,而不是砂。

您可以操纵pets.go中的字符串,而不是您的模板,但数据的呈现是模板的工作,而不是代码。

事实上,一些狗数据已经包含了一些演示文稿,也许它不应该。品种字段将多个品种编入一个字符串,并将它们分成一个字符串。这个单串的模式可能会邀请数据输入者在数据库中输入不同的格式:Labrador/Poodle,Labrador & Poodle,Labrador, Poodle,Labrador-Poodle混合,等等. 最好将品种存储为一个字符串(‘[] string’)的片段,而不是一个字符串,以避免这种格式的模糊性,并使品种搜索更加灵活,并更容易呈现它。

<$>[note] [label Try It Yourself] 尝试修改您的代码和模板来实施这些更改。

(详细解决方案)

 1[label pets.go]
 2. . .
 3type Pet struct {
 4    Name string
 5    Sex string
 6    Intact bool
 7    Age string
 8    Breed  []string
 9}
10
11func main() {
12    dogs := []Pet{
13    	{
14    		Name:   "Jujube",
15    		. . .
16    		Breed:  []string{"German Shepherd", "Pit Bull"},
17    	},
18    	{
19    		Name:   "Zephyr",
20    		. . .
21    		Breed:  []string{"German Shepherd", "Border Collie"},
22    	},
23    	{
24    		Name:   "Bruce Wayne",
25    		. . .
26    		Breed:  []string{"Chihuahua"},
27    	},
28    }
29    funcMap := template.FuncMap{
30    	"dec":     func(i int) int { return i - 1 },
31    	"replace": strings.ReplaceAll,
32    	"join":    strings.Join,
33    }
34    . . .
35} // end main
 1[label lastPet.tmpl]
 2{{- range (len . | dec | dec | slice . ) }}
 3---
 4Name:  {{ .Name }}
 5
 6Sex:   {{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if ("Female" | eq .Sex) }}spayed{{ else }}neutered{{ end }}{{ end }})
 7
 8Age:   {{ .Age }}
 9
10Breed: {{ join .Breed " & " }} ({{ if len .Breed | eq 1 }}purebred{{ else }}mixed breed{{ end }})
11{{ end -}}

] <$>

最后,让我们将同样的狗数据转化为HTML文档,看看为什么当您的模板的输出是HTML时,您应该始终使用html/template包。

步骤5:编写一个HTML模板

命令行工具可能会使用文本/模板来精心打印输出,而其他一些批量程序可能会使用它来从某些数据中创建结构齐全的文件,但Go模板通常也被用来渲染Web应用程序的HTML页面。

HTML 带来了一些特殊的挑战,而简单的文本没有。它使用角度包装元素(<td>), ampersands 标记实体(&nbsp;),并引用字符包装标签属性(<a href=https://www.digitalocean.com/`>`)。如果您的模板插入的任何数据都包含这些字符,使用 text/template 包可能会导致错误的 HTML - 或者更糟糕的是,代码注入。

这个包逃避了这些有问题的字符,插入了它们的安全的HTML等级(实体)。 数据中的一个ampersand变成了&amp;,一个左角的子变成了&lt;,等等。

让我们使用以前的狗数据来创建一个HTML文档,但首先我们将继续使用文本 / 模板来显示它的危险性。

打开「pets.go」并在Jujube的「Name」字段中添加以下突出文本:

 1[label pets.go]
 2        . . .
 3    dogs := []Pet{
 4    	{
 5    		Name:   "<script>alert(\"Gotcha!\");</script>Jujube",
 6    		Sex:    "Female",
 7    		Intact: false,
 8    		Age:    "10 months",
 9    		Breed:  "German Shepherd/Pit Bull",
10    	},
11    	{
12    		Name:   "Zephyr",
13    		Sex:    "Male",
14    		Intact: true,
15    		Age:    "13 years, 3 months",
16    		Breed:  "German Shepherd/Border Collie",
17    	},
18    	{
19    		Name:   "Bruce Wayne",
20    		Sex:    "Male",
21    		Intact: false,
22    		Age:    "3 years, 8 months",
23    		Breed:  "Chihuahua",
24    	},
25    }
26        . . .

现在,在一个名为petsHtml.tmpl的新文件中创建一个HTML模板:

 1[label petsHtml.tmpl]
 2<p><strong>Pets:</strong> {{ . | len }}</p>
 3{{ range . }}
 4<hr />
 5<dl>
 6    <dt>Name</dt>
 7    <dd>{{ .Name }}</dd>
 8    <dt>Sex</dt>
 9    <dd>{{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if (eq .Sex "Female") }}spayed{{ else }}neutered{{ end }}{{ end }})</dd>
10    <dt>Age</dt>
11    <dd>{{ .Age }}</dd>
12    <dt>Breed</dt>
13    <dd>{{ replace .Breed /  &  }}</dd>
14</dl>
15{{ end }}

保存HTML模板. 在运行pets.go之前,我们需要编辑tmpFile var,但让我们也编辑程序,将模板输出到一个文件,而不是终端。

 1[label pets.go]
 2    . . .
 3    funcMap := template.FuncMap{
 4    	"dec":     func(i int) int { return i - 1 },
 5    	"replace": strings.ReplaceAll,
 6    }
 7    var tmplFile = "petsHtml.tmpl"
 8    tmpl, err := template.New(tmplFile).Funcs(funcMap).ParseFiles(tmplFile)
 9    if err != nil {
10    	panic(err)
11    }
12    var f *os.File
13    f, err = os.Create("pets.html")
14    if err != nil {
15    	panic(err)
16    }
17    err = tmpl.Execute(f, dogs)
18    if err != nil {
19    	panic(err)
20    }
21    err = f.Close()
22    if err != nil {
23    	panic(err)
24    }
25} // end main

您正在打开一个名为 pets.html 的新文件,将其(而不是os.Stdout)转到tmpl.Execute,然后在完成时关闭文件。

现在运行go run pets.go来生成HTML文件,然后在浏览器中打开本地网页:

gotcha.png

这就是为什么你不应该使用文本/模板包来生成HTML,特别是当你不能完全信任你的模板数据的来源时。

除了在数据中逃避 HTML 字符外,html/template 套件功能与text/template 相似,并且具有相同的基名(template),这意味着您所要做的就是用html/template 替换text/template 导入。

1[label pets.go]
2package main
3
4import (
5    "os"
6    "strings"
7    "html/template"
8)
9. . .

保存文件,最后一次运行它,重写 pets.html. 然后在浏览器中更新 HTML 文件:

gotcha-sanitized.png

html/template包已经将注入的脚本作为网页中的单一文本。在文本编辑器中打开pets.html (或在浏览器中查看页面来源),然后看看第一个狗Jujube:

 1[label pets.html]
 2. . .
 3<dl>
 4        <dt>Name</dt>
 5        <dd>&lt;script&gt;alert(&#34;Gotcha!&#34;);&lt;/script&gt;Jujube</dd>
 6        <dt>Sex</dt>
 7        <dd>Female (spayed)</dd>
 8        <dt>Age</dt>
 9        <dd>10 months</dd>
10        <dt>Breed</dt>
11        <dd>German Shepherd &amp; Pit Bull</dd>
12</dl>
13. . .

html包取代了Jujube的名称中的角度和引用字符,以及品种中的ampersand。

结论

Go 模板是一个方便的工具,用于围绕任何数据包装任何文本. 您可以在命令行工具中使用它们来格式化输出,在 Web 应用程序中渲染 HTML,等等. 在本教程中,您使用了 Go 的内置模板包来从相同的数据中打印好的文本和 HTML 页面。

Published At
Categories with 技术
Tagged with
comments powered by Disqus