html/template(模板)

模板包(template)

  • import "html/template"

  • 概述

  • 索引

  • 示例

概述

模板包(html/template)实现了数据驱动的模板,以便在代码注入过程中安全地生成 HTML 输出。它提供了与包 text/template 相同的接口,只要输出是 HTML,就应该使用它来代替 text/template。

这里的文档侧重于包的安全特性。有关如何自行编写模板的信息,请参阅 text/template 的文档。

介绍

该软件包包装 text/template,以便您可以共享其模板 API 以安全地解析和执行 HTML 模板。

tmpl, err := template.New("name").Parse(...) // 错误检查已删除 err = tmpl.Execute(out, data)

如果成功,tmpl 现在将是注射安全的。否则, err 是 ErrorCode 文档中定义的错误。

HTML 模板将数据值视为应该被编码的纯文本,以便它们可以安全地嵌入到 HTML 文档中。转义是上下文的,因此操作可以出现在 JavaScript,CSS 和 URI 上下文中。

这个包使用的安全模型假定模板作者是可信的,而 Execute 的数据参数不是。更多细节在下面提供。

示例

import "text/template" ... t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")

生成

Hello, <script>alert('you have been pwned')</script>!

但在 html/template 中的上下文自动转义

import "html/template" ... t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")

生成安全的、转义过的 HTML 输出

Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!

上下文

该软件包可以理解 HTML,CSS,JavaScript 和 URI 。它给每个简单的操作流水线增加了清理功能,所以给出了摘录

<a href="/search?q={{.}}">{{.}}</a>

在解析时,每个 {{.}} 都会被覆盖,以根据需要添加转义函数。在这种情况下,它变成了

<a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>

urlescaper,attrescaper 和 htmlescaper 是内部转义函数的别名。

Errors

有关详细信息,请参阅 ErrorCode 的文档。

A fuller picture

在第一次阅读时可以跳过此包评论的其余部分;它包含了解转义上下文和错误消息所需的详细信息。大多数用户不需要了解这些细节。

上下文

假设 {{.}} 是O'Reilly: How are <i>you</i>?,下表显示了 {{.}} 在左侧上下文中使用时的显示方式。

Context {{.}} After {{.}} O'Reilly: How are &lt;i&gt;you&lt;/i&gt;? <a title='{{.}}'> O&#39;Reilly: How are you? <a href="/{{.}}"> O&#39;Reilly: How are %3ci%3eyou%3c/i%3e? <a href="?q={{.}}"> O&#39;Reilly%3a%20How%20are%3ci%3e...%3f <a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...? <a onx='f{{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?" <a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f

如果在不安全的上下文中使用,则可能会过滤掉该值:

Context {{.}} After <a href="{{.}}"> #ZgotmplZ

因为“O'Reilly:”不是像“http:”这样的允许协议。

如果{{.}}是无关紧要的词left,那么它可以更广泛地出现,

Context {{.}} After {{.}} left <a title='{{.}}'> left <a href='{{.}}'> left <a href='/{{.}}'> left <a href='?dir={{.}}'> left <a style="border-{{.}}: 4px"> left <a style="align: {{.}}"> left <a style="background: '{{.}}'> left <a style="background: url('{{.}}')> left <style>p.{{.}} {color:red}</style> left

非字符串值可以在 JavaScript 上下文中使用。如果是

struct{A,B string}{ "foo", "bar" }

在转义模板中

<script>var pair = {{.}};</script>

然后模板输出是

<script>var pair = {"A": "foo", "B": "bar"};</script>

请参阅包 json 以了解如何封装非字符串内容以嵌入 JavaScript 上下文中。

键入的字符串

默认情况下,此包假定所有管道都生成纯文本字符串。它添加了必要的转义管道阶段,以便在正确的上下文中正确安全地嵌入纯文本字符串。

如果数据值不是纯文本,则可以通过使用其类型对其进行标记来确保它不会过度转义。

来自 content.go 的 HTML,JS,URL 和其他类型可以携带免于转义的安全内容。

模板

Hello, {{.}}!

可以调用

tmpl.Execute(out, template.HTML(`<b>World</b>`))

来生成

Hello, <b>World</b>!

而不是

Hello, &lt;b&gt;World&lt;b&gt;!

如果 {{.}} 是常规字符串,则会生成。

安全模型

https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition定义此包使用的“safe”。

这个包假定模板作者是可信的,Execute 的数据参数不是,并试图在不受信任的数据面前保留下面的属性:

结构保留属性:“......当模板作者以安全模板语言编写HTML 标记时,浏览器会将输出的相应部分解释为标记,而不管不受信任数据的值如何,对于其他结构(如属性边界和 JS 和 CSS 字符串边界。“

代码效果属性:“...只有模板作者指定的代码才能运行,因为将模板输出注入到页面中,模板作者指定的所有代码都应该运行相同的结果。”

Least Surprise Property:“熟悉 HTML,CSS 和 JavaScript 的开发人员(或代码审查员),他们知道发生上下文自动转移应该能够查看{ {.}} 并正确推断出正在进行的清理。”

示例

package main import ( "html/template" "log" "os" ) func main() { const tpl = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>{{.Title}}</title> </head> <body> {{range .Items}}<div>{{ . }}</div>{{else}}<div><strong>no rows</strong></div>{{end}} </body> </html>` check := func(err error) { if err != nil { log.Fatal(err) } } t, err := template.New("webpage").Parse(tpl) check(err) data := struct { Title string Items []string }{ Title: "My page", Items: []string{ "My photos", "My blog", }, } err = t.Execute(os.Stdout, data) check(err) noItems := struct { Title string Items []string }{ Title: "My another page", Items: []string{}, } err = t.Execute(os.Stdout, noItems) check(err) }

示例(Autoescaping)

package main import ( "html/template" "log" "os" ) func main() { check := func(err error) { if err != nil { log.Fatal(err) } } t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) check(err) err = t.ExecuteTemplate(os.Stdout, "T", "<script>alert('you have been pwned')</script>") check(err) }

示例(Escape)

package main import ( "fmt" "html/template" "os" ) func main() { const s = `"Fran & Freddie's Diner" <tasty@example.com>` v := []interface{}{`"Fran & Freddie's Diner"`, ' ', `<tasty@example.com>`} fmt.Println(template.HTMLEscapeString(s)) template.HTMLEscape(os.Stdout, []byte(s)) fmt.Fprintln(os.Stdout, "") fmt.Println(template.HTMLEscaper(v...)) fmt.Println(template.JSEscapeString(s)) template.JSEscape(os.Stdout, []byte(s)) fmt.Fprintln(os.Stdout, "") fmt.Println(template.JSEscaper(v...)) fmt.Println(template.URLQueryEscaper(v...)) }

索引

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

  • func HTMLEscapeString(s string) string

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

  • func IsTrue(val interface{}) (truth, ok bool)

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

  • func JSEscapeString(s string) string

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

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

  • type CSS

  • type Error

  • func (e *Error) Error() string

  • type ErrorCode

  • type FuncMap

  • type HTML

  • type HTMLAttr

  • type JS

  • type JSStr

  • type Template

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

  • func New(name string) *Template

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

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

  • func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error)

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

  • func (t *Template) DefinedTemplates() string

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

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

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

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

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

  • func (t *Template) Name() string

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

  • func (t *Template) Option(opt ...string) *Template

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

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

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

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

  • type URL

示例

Package Template (Block) Template (Glob) Template (Helpers) Template (Parsefiles) Template (Share) Package (Autoescaping) Package (Escape)

包文件

attr.go content.go context.go css.go doc.go error.go escape.go html.go js.go template.go transition.go url.go

func HTMLEscape(查看源代码)

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

HTMLEscape 写入到明文数据 b 的转义 HTML 等价物中。

func HTMLEscapeString(查看源代码)

func HTMLEscapeString(s string) string

HTMLEscapeString 返回纯文本数据的转义 HTML 等价物。

func HTMLEscaper(查看源代码)

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

HTMLEscaper 返回其参数文本表示的转义 HTML 等价物。

func IsTrue(查看源代码)

func IsTrue(val interface{}) (truth, ok bool)

IsTrue 报告该值是否为 'true',意味着它的类型不为零,以及该值是否具有有意义的真值。这是 if 和其他此类行为所使用的真相的定义。

func JSEscape(查看源代码)

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

JSEscape写入 w 的纯文本数据 b 的逃逸 JavaScript 等价物。

func JSEscapeString(查看源代码)

func JSEscapeString(s string) string

JSEscapeString 返回纯文本数据的转义 JavaScript 等价物。

func JSEscaper(查看源代码)

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

JSEscaper 返回其参数的文本表示的转义 JavaScript 等价物。

func URLQueryEscaper(查看源代码)

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

URLQueryEscape r 以适合于嵌入到 URL 查询中的形式返回其参数的文本表示的转义值。

type CSS(查看源代码)

CSS 封装了与以下任何匹配的已知安全内容:

1. CSS3样式表的制作,例如`p {color:purple}`。 2. CSS3规则生成,例如`a [href =〜“https:”]。foo #bar`。 3. CSS3声明制作,如`color:red; 保证金:2px`。 4. CSS3值的产生,例如`rgba(0,0,255,127)`。

请参阅http://www.w3.org/TR/css3-syntax/#parsinghttps://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style

使用此类型会带来安全风险:封装内容应来自可信来源,因为它将逐字包含在模板输出中。

type CSS string

type Error(查看源代码)

错误描述了模板转义期间遇到的问题。

type Error struct { // ErrorCode描述了错误的类型。 ErrorCode ErrorCode // 如果已知,节点是导致问题的节点。 // 如果不是nil,它将覆盖Name和Line。 Node parse.Node // Name是遇到错误的模板的名称。 Name string // Line是模板源中错误的行号或0。 Line int // 描述是对问题的可读描述。 Description string }

func (*Error) Error(查看源代码)

func (e *Error) Error() string

type ErrorCode(查看源代码)

ErrorCode 是一种错误的代码。

type ErrorCode int

我们为转义模板时显示的每个错误定义代码,但转义模板也可能在运行时失败。

输出:“ZgotmplZ”示例:

<img src="{{.X}}"> where {{.X}} evaluates to `javascript:...`

讨论:

“ZgotmplZ”是一个特殊值,表示不安全的内容达到了 运行时的CSS或URL上下文。示例的输出将是 <img src =“#ZgotmplZ”> 如果数据来自受信任的来源,请使用内容类型来免除它 来自过滤:URL(`javascript:...`)。

const ( // OK表示没有错误。 OK ErrorCode = iota // ErrAmbigContext:“......出现在URL中的模糊上下文中” // 示例: // <a href=" // {{if .C}} // /path/ // {{else}} // /search?q= // {{end}} // {{.X}} // "> // 讨论: // {{.X}}位于不明确的网址环境中,因为取决于 {{.C}}, // 它可以是URL后缀或查询参数。 // 将{{.X}}移入条件可消除歧义: // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}"> ErrAmbigContext // ErrBadHTML:“expected space, attr name, or end of tag, but got ...(预期的空间,attr名称或标签的结尾,但得到......)”, // "... in unquoted attr", "... in attribute name(...在未加引号的attr“,”...在属性名称中)" // 示例: // <a href = /search?q=foo> // <href=foo> // <form na<e=...> // <option selected< // 讨论: // 这通常是由于HTML元素中的拼写错误,而是一些符文 // 在标记名称,属性名称和不带引号的属性中被禁止 // 值因为它们可以解决解析器的歧义。 // 引用所有属性是最好的策略。 ErrBadHTML // ErrBranchEnd: "{{if}} branches end in different contexts({{if}}分支在不同的上下文中结束)" // 示例: // {{if .C}}<a href="{{end}}{{.X}} // 讨论: // 包html/template静态检查每条路径 // {{if}}, {{range}}, 或 {{with}} 转义任何后续的管道。 // 该示例不明确,因为{{.X}}可能是HTML文本节点, // 或HTML属性中的URL前缀。 {{。X}}的上下文是 // 用来弄清楚如何转义,但这种情况取决于 // {{.C}}的运行时值,它不是静态知道的。 // // 问题通常是缺少引号或角度 // 括号,或者可以通过重构放置两个上下文来避免 // 进入if,range或with的不同分支。 如果问题 // 在一个永远不应为空的集合中的{{range}} // 添加虚拟{{else}}可以提供帮助。 ErrBranchEnd // ErrEndContext: "... ends in a non-text context: ...(......以非文本语境结束:......)" // 示例: // <div // <div title="no close quote> // <script>f() // 讨论: // 执行的模板应该生成HTML的DocumentFragment。 // 以关闭标记结束的模板将触发此错误。 // 不应在HTML上下文中使用的模板 // 生成不完整的碎片不应该直接执行。 // // {{define "main"}} <script>{{template "helper"}}</script> {{end}} // {{define "helper"}} document.write(' <div title=" ') {{end}} // // "helper" 不会产生有效的文档片段,所以应该如此 // 不能直接执行。 ErrEndContext // ErrNoSuchTemplate: "no such template ...(没有这样的模板...)" // 示例: // {{define "main"}}<div {{template "attrs"}}>{{end}} // {{define "attrs"}}href="{{.URL}}"{{end}} // 讨论: // html/template包通过模板调用查看计算 // 上下文。 // 这里调用“attrs”中的{{.URL}}必须被视为URL // 来自“main”,但如果未定义“attrs”,您将收到此错误 // 当解析“main”时。 ErrNoSuchTemplate // ErrOutputContext: "cannot compute output context for template ...(无法计算模板的输出上下文...)" // 示例: // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}} // 讨论: // 递归模板不会在与其相同的上下文中结束 // 启动,无法计算可靠的输出上下文。 // 在命名模板中查找拼写错误。 // 如果不应在命名的开始上下文中调用模板, // 在意外的上下文中查找对该模板的调用。 // 也许重构递归模板不是递归的。 ErrOutputContext // ErrPartialCharset: "unfinished JS regexp charset in ...(未完成的JS regexp charset in ...)" // 示例: // <script>var pattern = /foo[{{.Chars}}]/</script> // 讨论: // html/template包不支持插入常规 // 表达式文字字符集。 ErrPartialCharset // ErrPartialEscape: "unfinished escape sequence in ...(未完成的转义序列...)" // 示例: // <script>alert("\{{.X}}")</script> // 讨论: // html/template包不支持以下操作 // 反斜杠。 // 这通常是一个错误,有更好的解决方案; 对于 // 示例 // <script>alert("{{.X}}")</script> // 应该行得通,如果{{.X}}是一个部分转义序列,如 // "xA0", mark the whole sequence as safe content(将整个序列标记为安全内容): JSStr(`\xA0`) ErrPartialEscape // ErrRangeLoopReentry: "on range loop re-entry: ...(在范围循环重新进入:...)" // 示例: // <script>var x = [{{range .}}'{{.}},{{end}}]</script> // 讨论: // 如果通过范围的迭代将导致它以下列结尾 // 与早期传递不同的上下文,没有单一的上下文。 // 在示例中,缺少引号,因此不清楚 // 是{{.}}是在JS字符串内还是在JS值中 // 上下文。 第二次迭代会产生类似的东西 // // <script>var x = ['firstValue,'secondValue]</script> ErrRangeLoopReentry // ErrSlashAmbig: '/' 可以开始分裂或正则表达式。 // 示例: // <script> // {{if .C}}var x = 1{{end}} // /-{{.N}}/i.test(x) ? doThis : doThat( // </script> // 讨论: // 上面的例子可以生成 `var x = 1/-2/i.test(s)...` // 其中第一个'/'是数学除法运算符或它 // 可以生成`/-2/i.test(s)`,其中第一个'/'开启一个 // regexp文字。 // 寻找分支内缺少的分号,也许添加 // 括号,以明确您想要的解释。 ErrSlashAmbig // ErrPredefinedEscaper: "预定义的escaper ...在模板中不允许" // 示例: // <div class={{. | html}}>Hello<div> // 讨论: // 包html/模template板已经在上下文中将所有管道转义为 // 生成HTML输出以防止代码注入。 手动转义 // 使用预定义的转发器“html”或“urlquery”进行管道输出 // 不必要的,可能会影响转义的正确性或安全性 // Go 1.8及更早版本中的管道输出。 // // 在大多数情况下,例如给定的示例,可以通过解决此错误 // 只需从管道中删除预定义的escaper并让它 // contextual autoescaper处理管道的转义。 其他 // 实例,其中预定义的escaper发生在下列的中间 // 后续命令期望转义输入的管道,例如, // {{.X | html | makeALink}} // 当makeALink // 返回 `<a href="`+input+`">link</a>` // 考虑重构周围的模板以利用 // 上下文自动转义,即 // <a href="{{.X}}">link</a> // // 为了便于迁移到Go 1.9及更高版本,“html”和“urlquery”将会 // 继续被允许作为管道中的最后一个命令。 但是,如果 // 管道发生在不带引号的属性值上下文中,“html”是 // 不允许。 避免在新模板中完全使用“html”和“urlquery”。 ErrPredefinedEscaper )

type FuncMap(查看源代码)

FuncMap 是定义从名称到函数映射的映射的类型。每个函数必须具有单个返回值,或者其中第二个具有类型错误的返回值。在这种情况下,如果第二个(error)参数在执行过程中评估为非零,则执行结束并执行返回该错误。FuncMap 与“text/template”中的FuncMap具有相同的基本类型,在这里复制,因此客户端不需要导入“text/template”。

type FuncMap map[string]interface{}

type HTML(查看源代码)

HTML 封装了一个已知的安全HTML文档片段。它不应该用于来自第三方的HTML,或 HTML 不带标签或注释的 HTML。由这个包转义出来的声音 HTML清理器和模板的输出,对于使用 HTML 来说是很好的。

使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。

type HTML string

type HTMLAttr(查看源代码)

HTMLAttr 封装来自信任来源的 HTML 属性,例如dir="ltr"

使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。

type HTMLAttr string

type JS(查看源代码)

例如,JS封装了一个已知的安全EcmaScript5表达式(x + y \* z())。模板作者负责确保类型化表达式不会打破预期的优先顺序,并且在传递像“{ foo: bar() }\n'foo'”这样的表达式时不存在语句/表达式模糊性,这两个表达式都是有效的表达和一个有着非常不同含义的有效程序。

使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。

使用 JS 来包含有效但不可信的 JSON 是不安全的。一个安全的选择是使用 json.Unmarshal解析JSON ,然后将生成的对象传递到模板中,在 JavaScript 上下文中将其转换为清理过的 JSON 。

type JS string

type JSStr(查看源代码)

JSStr 在 JavaScript 表达式中封装了一系列用于嵌入在引号之间的字符序列。该字符串必须与一系列 StringCharacters 匹配:

StringCharacter :: SourceCharacter but not `\` or LineTerminator | EscapeSequence

请注意,LineContinuations 是不允许的。 JSStr("foo\nbar") 是很好的,但 JSStr("foo\\nbar") 不是。

使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。

type JSStr string

type Template(查看源代码)

模板是一个来自“text/template(文本/模板)”的专用模板,可以生成安全的HTML文档片段。

type Template struct { // 底层模板的解析树,更新为HTML安全。 Tree *parse.Tree // 包含已过滤或未导出的字段 }

示例(Block)

package main import ( "html/template" "log" "os" "strings" ) func main() { const ( master = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}` overlay = `{{define "list"}} {{join . ", "}}{{end}} ` ) var ( funcs = template.FuncMap{"join": strings.Join} guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"} ) masterTmpl, err := template.New("master").Funcs(funcs).Parse(master) if err != nil { log.Fatal(err) } overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay) if err != nil { log.Fatal(err) } if err := masterTmpl.Execute(os.Stdout, guardians err != nil { log.Fatal(err) } if err := overlayTmpl.Execute(os.Stdout, guardians err != nil { log.Fatal(err) } }

示例(Glob)

这里我们演示从目录加载一组模板。

代码:

// 在这里,我们创建一个临时目录并使用我们的示例填充 // 模板定义文件; 通常模板文件已经存在 // 存在于程序已知的某个位置。 dir := createTestDir([]templateFile{ // T0.tmpl is a plain template file that just invokes T1. {"T0.tmpl", `T0 invokes T1: {{template "T1"}})`}, // T1.tmpl defines a template, T1 that invokes T2. {"T1.tmpl", `{{define "T1"}}T1 invokes T2: {{template "T2"}}){{end}}`}, // T2.tmpl defines a template T2. {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, }) // 存在于程序已知的某个位置。 defer os.RemoveAll(dir) // pattern是用于查找所有模板文件的glob模式。 pattern := filepath.Join(dir, "*.tmpl") // 这里开始正确的例子。 // T0.tmpl是匹配的第一个名字,因此它成为起始模板, // ParseGlob返回的值。 tmpl := template.Must(template.ParseGlob(pattern)) err := tmpl.Execute(os.Stdout, nil) if err != nil { log.Fatalf("template execution: %s", err) }

输出:

T0 invokes T1: (T1 invokes T2: (This is T2))

示例(Helpers)

此示例演示了共享某些模板并在不同环境中使用它们的一种方法。在这个变体中,我们手动添加多个驱动模板到现有的一组模板。

代码:

// 在这里,我们创建一个临时目录并使用我们的示例填充它 // 模板定义文件; 通常模板文件已经存在 // 存在于程序已知的某个位置。 dir := createTestDir([]templateFile{ // T1.tmpl定义了一个调用T2的模板T1。 {"T1.tmpl", `{{define "T1"}}T1 invokes T2: {{template "T2"}}){{end}}`}, // T2.tmpl定义模板T2。 {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, }) // 测试后清理; 另一个以运行为例的怪癖。 defer os.RemoveAll(dir) // pattern是用于查找所有模板文件的glob模式。 pattern := filepath.Join(dir, "*.tmpl") // 这里开始正确的例子。 // 加载Helpers。 templates := template.Must(template.ParseGlob(pattern)) // 添加一个驱动程序模板到一堆; 我们使用显式模板定义来完成此操作。 _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: {{template `T1`}})\n{{end}}") if err != nil { log.Fatal("parsing driver1: ", err) } // 添加另一个驱动模板。 _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: {{template `T2`}})\n{{end}}") if err != nil { log.Fatal("parsing driver2: ", err) } // 我们在执行之前加载所有模板。 此包不需要 // 那个行为,但html/template的转义确实存在,所以这是一个好习惯。 err = templates.ExecuteTemplate(os.Stdout, "driver1", nil) if err != nil { log.Fatalf("driver1 execution: %s", err) } err = templates.ExecuteTemplate(os.Stdout, "driver2", nil) if err != nil { log.Fatalf("driver2 execution: %s", err) }

输出:

Driver 1 calls T1: (T1 invokes T2: (This is T2)) Driver 2 calls T2: (This is T2)

示例(Parsefiles)

这里我们演示从不同目录中的文件加载一组模板

代码:

// 在这里,我们创建不同的临时目录并使用我们的示例填充它们 // 模板定义文件; 通常模板文件已经 // 存在于程序已知的某个位置。 dir1 := createTestDir([]templateFile{ // T1.tmpl是一个只调用T2的普通模板文件。 {"T1.tmpl", `T1 invokes T2: {{template "T2"}})`}, }) dir2 := createTestDir([]templateFile{ // T2.tmpl定义模板T2。 {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, }) // 测试后清理; 另一个以跑步为例的怪癖。 defer func(dirs ...string) { for _, dir := range dirs { os.RemoveAll(dir) } }(dir1, dir2) // 这里开始正确的例子。 // 我们只解析dir1/T0和dir2/T2 paths := []string{ filepath.Join(dir1, "T1.tmpl"), filepath.Join(dir2, "T2.tmpl"), } tmpl := template.Must(template.ParseFiles(paths...)) err := tmpl.Execute(os.Stdout, nil) if err != nil { log.Fatalf("template execution: %s", err) }

输出:

T1 invokes T2: (This is T2)

示例(Share)

本示例演示如何将一组驱动程序模板与不同的辅助程序模板集一起使用。

编码:

// 在这里,我们创建一个临时目录并使用我们的示例填充它 // 模板定义文件; 通常模板文件已经存在 // 存在于程序已知的某个位置。 dir := createTestDir([]templateFile{ // T0.tmpl是一个只调用T1的普通模板文件。 {"T0.tmpl", "T0 {{.}} version) invokes T1: {{template `T1`}})\n"}, // T1.tmpl定义了一个调用T2的模板T1。 注意T2未定义 {"T1.tmpl", `{{define "T1"}}T1 invokes T2: {{template "T2"}}){{end}}`}, }) // 测试后清理; 另一个以运行为例的怪癖。 defer os.RemoveAll(dir) // pattern是用于查找所有模板文件的glob模式。 pattern := filepath.Join(dir, "*.tmpl") // 这里开始正确的例子。 // 加载驱动程序。 drivers := template.Must(template.ParseGlob(pattern)) // 我们必须定义T2模板的实现。 首先我们拷贝 // 驱动程序,然后将T2的定义添加到模板名称空间。 // 1.拷贝帮助器集以创建一个新的名称空间,从中运行它们。 first, err := drivers.Clone() if err != nil { log.Fatal("cloning helpers: ", err) } // 2.定义T2,版本A并解析它。 _, err = first.Parse("{{define `T2`}}T2, version A{{end}}") if err != nil { log.Fatal("parsing T2: ", err) } // 现在重复整个过程,使用不同版本的T2。 // 1.拷贝驱动程序。 second, err := drivers.Clone() if err != nil { log.Fatal("cloning drivers: ", err) } // 2.定义T2,版本B,并解析它。 _, err = second.Parse("{{define `T2`}}T2, version B{{end}}") if err != nil { log.Fatal("parsing T2: ", err) } // 以相反的顺序执行模板以验证 // 首先不受第二个影响。 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) }

输出:

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

func Must(查看源代码)

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

Must 必须是一个帮助函数,如果错误不为零,它会将函数调用返回 (*Template,error) 并发生混乱。它旨在用于变量初始化,如

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

func New(查看源代码)

func New(name string) *Template

新分配给定名称的新 HTML 模板。

func ParseFiles(查看源代码)

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

ParseFiles 创建一个新的模板并从命名文件中解析模板定义。返回的模板名称将具有第一个文件的(基本)名称和(已解析)内容。必须至少有一个文件。如果发生错误,解析将停止,返回的* Template将为零。

当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。例如,ParseFiles("a/foo", "b/foo") 存储"b/foo" 作为名为“foo”的模板,而“a/foo”不可用。

func ParseGlob(查看源代码)

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

ParseGlob创建一个新的模板并分析模式标识的文件中的模板定义,该文件必须至少匹配一个文件。返回的模板将具有与该模式匹配的第一个文件的(基本)名称和(已分析)内容。ParseGlob相当于使用模式匹配的文件列表调用ParseFiles。

当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。

func (*Template) AddParseTree(查看源代码)

func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error)

AddParseTree 使用名称和分析树创建一个新模板并将其与 t 关联。

如果 t 或任何关联的模板已经被执行,它会返回一个错误。

func (*Template) Clone(查看源代码)

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

克隆返回模板的副本,包括所有关联的模板。实际表示不会被复制,但是相关模板的名称空间是,因此对副本中的分析进一步调用会将模板添加到副本,但不会添加到原始模板。可以使用克隆来制备通用模板,并在制作克隆后通过添加变体来将它们与其他模板的变体定义一起使用。

如果 t 已经执行,它会返回一个错误。

func (*Template) DefinedTemplates(查看源代码)

func (t *Template) DefinedTemplates() string

DefinedTemplates 返回一个字符串,列出定义的模板,以字符串“; defined templates are: ”为前缀。如果没有,则返回空字符串。用于生成错误消息。

func (*Template) Delims(查看源代码)

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

Delims 将动作分隔符设置为指定的字符串,以便在随后调用 Parse, ParseFiles 或 ParseGlob 时使用。嵌套模板定义将继承这些设置。一个空的分隔符表示相应的默认值:{{或}}。返回值是模板,因此可以将调用链接起来。

func (*Template) Execute(查看源代码)

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

执行将解析的模板应用于指定的数据对象,将输出写入 wr 。如果执行模板或写入其输出时发生错误,则执行停止,但部分结果可能已写入输出写入器。模板可以并行安全地执行,但如果并行执行共享 Writer ,输出可能会交错。

func (*Template) ExecuteTemplate(查看源代码)

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

ExecuteTemplate 将与具有给定名称的 t 关联的模板应用于指定的数据对象,并将输出写入 wr 。如果执行模板或写入其输出时发生错误,则执行停止,但部分结果可能已写入输出写入器。模板可以并行安全地执行,但如果并行执行共享 Writer ,输出可能会交错。

func (*Template) Funcs(查看源代码)

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

Funcs 将参数图的元素添加到模板的功能图中。它必须在解析模板之前调用。如果映射中的值不是具有适当返回类型的函数,则会发生混乱。但是,覆盖地图的元素是合法的。返回值是模板,因此可以将调用链接起来。

func (*Template) Lookup(查看源代码)

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

Lookup 返回与 t 关联的给定名称的模板,如果没有这样的模板,则返回 nil。

func (*Template) Name(查看源代码)

func (t *Template) Name() string

名称返回模板的名称。

func (*Template) New(查看源代码)

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

新分配一个新的 HTML 模板与给定的和相同的分隔符相关联。该关联是可传递的,它允许一个模板通过 {{template}} 动作调用另一个模板。

func (*Template) Option(查看源代码)

func (t *Template) Option(opt ...string) *Template

选项为模板设置选项。选项由字符串描述,可以是简单字符串或 "key=value"。选项字符串中最多只能有一个等号。如果选项字符串无法识别或无效,则选项会出现混乱。

已知选项:

missingkey:如果地图使用地图中不存在的键索引,则控制执行过程中的行为。

"missingkey=default" or "missingkey=invalid" The default behavior: Do nothing and continue execution. If printed, the result of the index operation is the string "<no value>". "missingkey=zero" The operation returns the zero value for the map type's element. "missingkey=error" Execution stops immediately with an error.

func (*Template) Parse(查看源代码)

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

解析将文本解析为t的模板体。文本中的命名模板定义 ({{define ...}} 或 {{block ...}}语句) 定义了与 t 关联的附加模板,并从 t 本身的定义中删除。

在第一次使用“执行”或任何相关模板之前,可以在连续调用 Parse 时重新定义模板。包含仅包含空格和注释的主体的模板定义被视为空白,并且不会替换现有模板的主体。这允许使用 Parse 添加新的命名模板定义而不覆盖主模板主体。

func (*Template) ParseFiles(查看源代码)

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

ParseFiles 解析命名文件并将生成的模板与 t 关联。如果发生错误,解析将停止并且返回的模板为零;否则是 t 。必须至少有一个文件。

当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。

如果t或任何关联的模板已经执行,则 ParseFiles 将返回一个错误。

func (*Template) ParseGlob(查看源代码)

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

ParseGlob 解析由模式标识的文件中的模板定义,并将生成的模板与 t 关联。该模式由 filepath.Glob 处理,并且必须至少匹配一个文件。ParseGlob 相当于使用模式匹配的文件列表调用 t.ParseFiles。

当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。

如果 t 或任何关联的模板已被执行,则ParseGlob 将返回错误。

func (*Template) Templates(查看源代码)

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

模板返回与t关联的模板的一部分,包括 t 本身。

type URL(查看源代码)

URL 封装了已知的安全 URL 或 URL 子字符串(请参阅RFC 3986)。类似于javascript:checkThatFormNotEditedBeforeLeavingPage()受信任源的 URL 应该放在页面中,但默认情况下动态javascript:URL 会被过滤掉,因为它们是一个经常被利用的注入向量。

使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。

type URL string