©
이 문서에서는 PHP 중국어 웹사이트 매뉴얼 풀어 주다
import "html/template"
概述
索引
示例
模板包(html/template)实现了数据驱动的模板,以便在代码注入过程中安全地生成HTML输出。它提供了与包文本/模板相同的接口,只要输出是HTML,就应该使用它来代替文本/模板。
这里的文档侧重于包的安全特性。有关如何自行编写模板的信息,请参阅文本/模板的文档。
该软件包包装文本/模板,以便您可以共享其模板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 /模板中的上下文自动转义
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, <script>alert('you have been pwned')</script>!
该软件包可以理解 HTML,CSS,JavaScript和URI 。它给每个简单的操作流水线增加了清理功能,所以给出了摘录
<a href="/search?q={{.}}">{{.}}</a>
在解析时,每个 {{.}} 都会被覆盖,以根据需要添加转义函数。在这种情况下,它变成了
<a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>
urlescaper,attrescaper和htmlescaper 是内部转义函数的别名。
有关详细信息,请参阅ErrorCode的文档。
在第一次阅读时可以跳过此包评论的其余部分;它包含了解转义上下文和错误消息所需的详细信息。大多数用户不需要了解这些细节。
假设 {{.}} 是O'Reilly: How are <i>you</i>?
,下表显示了 {{.}} 在左侧上下文中使用时的显示方式。
Context {{.}} After{{.}} O'Reilly: How are <i>you</i>?<a title='{{.}}'> O'Reilly: How are you?<a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e?<a href="?q={{.}}"> O'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, <b>World<b>!
如果{{.}}是常规字符串,则会生成。
https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition定义此包使用的“safe”。
这个包假定模板作者是可信的,Execute的数据参数不是,并试图在不受信任的数据面前保留下面的属性:
结构保留属性:“......当模板作者以安全模板语言编写HTML标记时,浏览器会将输出的相应部分解释为标记,而不管不受信任数据的值如何,对于其他结构(如属性边界和JS和CSS字符串边界。“
代码效果属性:“...只有模板作者指定的代码才能运行,因为将模板输出注入到页面中,模板作者指定的所有代码都应该运行相同的结果。”
Least Surprise Property:“熟悉HTML,CSS 和 JavaScript 的开发人员(或代码审查员),他们知道发生上下文自动转移应该能够查看{ {.}} 并正确推断出正在进行的清理。”
package mainimport ("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)}
package mainimport ("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)}
package mainimport ("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(w io.Writer, b []byte)
HTMLEscape 写入到明文数据 b 的转义HTML 等价物中。
func HTMLEscapeString(s string) string
HTMLEscapeString 返回纯文本数据的转义 HTML 等价物。
func HTMLEscaper(args ...interface{}) string
HTMLEscaper 返回其参数文本表示的转义 HTML 等价物。
func IsTrue(val interface{}) (truth, ok bool)
IsTrue报告该值是否为'true',意味着它的类型不为零,以及该值是否具有有意义的真值。这是 if 和其他此类行为所使用的真相的定义。
func JSEscape(w io.Writer, b []byte)
JSEscape写入 w 的纯文本数据 b 的逃逸 JavaScript 等价物。
func JSEscapeString(s string) string
JSEscapeString 返回纯文本数据的转义 JavaScript 等价物。
func JSEscaper(args ...interface{}) string
JSEscaper 返回其参数的文本表示的转义 JavaScript 等价物。
func URLQueryEscaper(args ...interface{}) string
URLQueryEscape r 以适合于嵌入到 URL 查询中的形式返回其参数的文本表示的转义值。
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/#parsing和https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style
使用此类型会带来安全风险:封装内容应来自可信来源,因为它将逐字包含在模板输出中。
type CSS string
错误描述了模板转义期间遇到的问题。
type Error struct { // ErrorCode describes the kind of error. ErrorCode ErrorCode // Node is the node that caused the problem, if known. // If not nil, it overrides Name and Line. Node parse.Node // Name is the name of the template in which the error was encountered. Name string // Line is the line number of the error in the template source or 0. Line int // Description is a human-readable description of the problem. Description string}
func (e *Error) Error() string
ErrorCode 是一种错误的代码。
type ErrorCode int
我们为转义模板时显示的每个错误定义代码,但转义模板也可能在运行时失败。
输出:“ZgotmplZ”示例:
<img src="{{.X}}">where {{.X}} evaluates to `javascript:...`
讨论:
"ZgotmplZ" is a special value that indicates that unsafe content reached a CSS or URL context at runtime. The output of the example will be <img src="#ZgotmplZ">If the data comes from a trusted source, use content types to exempt itfrom filtering: URL(`javascript:...`).
const ( // OK indicates the lack of an error. OK ErrorCode = iota // ErrAmbigContext: "... appears in an ambiguous context within a URL" // Example: // <a href=" // {{if .C}} // /path/ // {{else}} // /search?q= // {{end}} // {{.X}} // "> // Discussion: // {{.X}} is in an ambiguous URL context since, depending on {{.C}}, // it may be either a URL suffix or a query parameter. // Moving {{.X}} into the condition removes the ambiguity: // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}"> ErrAmbigContext // ErrBadHTML: "expected space, attr name, or end of tag, but got ...", // "... in unquoted attr", "... in attribute name" // Example: // <a href = /search?q=foo> // <href=foo> // <form na<e=...> // <option selected< // Discussion: // This is often due to a typo in an HTML element, but some runes // are banned in tag names, attribute names, and unquoted attribute // values because they can tickle parser ambiguities. // Quoting all attributes is the best policy. ErrBadHTML // ErrBranchEnd: "{{if}} branches end in different contexts" // Example: // {{if .C}}<a href="{{end}}{{.X}} // Discussion: // Package html/template statically examines each path through an // {{if}}, {{range}}, or {{with}} to escape any following pipelines. // The example is ambiguous since {{.X}} might be an HTML text node, // or a URL prefix in an HTML attribute. The context of {{.X}} is // used to figure out how to escape it, but that context depends on // the run-time value of {{.C}} which is not statically known. // // The problem is usually something like missing quotes or angle // brackets, or can be avoided by refactoring to put the two contexts // into different branches of an if, range or with. If the problem // is in a {{range}} over a collection that should never be empty, // adding a dummy {{else}} can help. ErrBranchEnd // ErrEndContext: "... ends in a non-text context: ..." // Examples: // <div // <div title="no close quote> // <script>f() // Discussion: // Executed templates should produce a DocumentFragment of HTML. // Templates that end without closing tags will trigger this error. // Templates that should not be used in an HTML context or that // produce incomplete Fragments should not be executed directly. // // {{define "main"}} <script>{{template "helper"}}</script> {{end}} // {{define "helper"}} document.write(' <div title=" ') {{end}} // // "helper" does not produce a valid document fragment, so should // not be Executed directly. ErrEndContext // ErrNoSuchTemplate: "no such template ..." // Examples: // {{define "main"}}<div {{template "attrs"}}>{{end}} // {{define "attrs"}}href="{{.URL}}"{{end}} // Discussion: // Package html/template looks through template calls to compute the // context. // Here the {{.URL}} in "attrs" must be treated as a URL when called // from "main", but you will get this error if "attrs" is not defined // when "main" is parsed. ErrNoSuchTemplate // ErrOutputContext: "cannot compute output context for template ..." // Examples: // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}} // Discussion: // A recursive template does not end in the same context in which it // starts, and a reliable output context cannot be computed. // Look for typos in the named template. // If the template should not be called in the named start context, // look for calls to that template in unexpected contexts. // Maybe refactor recursive templates to not be recursive. ErrOutputContext // ErrPartialCharset: "unfinished JS regexp charset in ..." // Example: // <script>var pattern = /foo[{{.Chars}}]/</script> // Discussion: // Package html/template does not support interpolation into regular // expression literal character sets. ErrPartialCharset // ErrPartialEscape: "unfinished escape sequence in ..." // Example: // <script>alert("\{{.X}}")</script> // Discussion: // Package html/template does not support actions following a // backslash. // This is usually an error and there are better solutions; for // example // <script>alert("{{.X}}")</script> // should work, and if {{.X}} is a partial escape sequence such as // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`) ErrPartialEscape // ErrRangeLoopReentry: "on range loop re-entry: ..." // Example: // <script>var x = [{{range .}}'{{.}},{{end}}]</script> // Discussion: // If an iteration through a range would cause it to end in a // different context than an earlier pass, there is no single context. // In the example, there is missing a quote, so it is not clear // whether {{.}} is meant to be inside a JS string or in a JS value // context. The second iteration would produce something like // // <script>var x = ['firstValue,'secondValue]</script> ErrRangeLoopReentry // ErrSlashAmbig: '/' could start a division or regexp. // Example: // <script> // {{if .C}}var x = 1{{end}} // /-{{.N}}/i.test(x) ? doThis : doThat(); // </script> // Discussion: // The example above could produce `var x = 1/-2/i.test(s)...` // in which the first '/' is a mathematical division operator or it // could produce `/-2/i.test(s)` in which the first '/' starts a // regexp literal. // Look for missing semicolons inside branches, and maybe add // parentheses to make it clear which interpretation you intend. ErrSlashAmbig // ErrPredefinedEscaper: "predefined escaper ... disallowed in template" // Example: // <div class={{. | html}}>Hello<div> // Discussion: // Package html/template already contextually escapes all pipelines to // produce HTML output safe against code injection. Manually escaping // pipeline output using the predefined escapers "html" or "urlquery" is // unnecessary, and may affect the correctness or safety of the escaped // pipeline output in Go 1.8 and earlier. // // In most cases, such as the given example, this error can be resolved by // simply removing the predefined escaper from the pipeline and letting the // contextual autoescaper handle the escaping of the pipeline. In other // instances, where the predefined escaper occurs in the middle of a // pipeline where subsequent commands expect escaped input, e.g. // {{.X | html | makeALink}} // where makeALink does // return `<a href="`+input+`">link</a>` // consider refactoring the surrounding template to make use of the // contextual autoescaper, i.e. // <a href="{{.X}}">link</a> // // To ease migration to Go 1.9 and beyond, "html" and "urlquery" will // continue to be allowed as the last command in a pipeline. However, if the // pipeline occurs in an unquoted attribute value context, "html" is // disallowed. Avoid using "html" and "urlquery" entirely in new templates. ErrPredefinedEscaper)
FuncMap是定义从名称到函数映射的映射的类型。每个函数必须具有单个返回值,或者其中第二个具有类型错误的返回值。在这种情况下,如果第二个(error)参数在执行过程中评估为非零,则执行结束并执行返回该错误。FuncMap与“text/template”中的FuncMap具有相同的基本类型,在这里复制,因此客户端不需要导入“text/template”。
type FuncMap map[string]interface{}
HTML 封装了一个已知的安全HTML文档片段。它不应该用于来自第三方的HTML,或 HTML 不带标签或注释的 HTML。由这个包转义出来的声音 HTML清理器和模板的输出,对于使用 HTML 来说是很好的。
使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。
type HTML string
HTMLAttr 封装来自信任来源的 HTML 属性,例如dir="ltr"
。
使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。
type HTMLAttr string
例如,JS封装了一个已知的安全EcmaScript5表达式(x + y \* z())
。模板作者负责确保类型化表达式不会打破预期的优先顺序,并且在传递像“{ foo: bar() }\n'foo'”这样的表达式时不存在语句/表达式模糊性,这两个表达式都是有效的表达和一个有着非常不同含义的有效程序。
使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。
使用 JS 来包含有效但不可信的 JSON 是不安全的。一个安全的选择是使用 json.Unmarshal解析JSON ,然后将生成的对象传递到模板中,在 JavaScript 上下文中将其转换为清理过的 JSON 。
type JS string
JSStr 在 JavaScript 表达式中封装了一系列用于嵌入在引号之间的字符序列。该字符串必须与一系列 StringCharacters 匹配:
StringCharacter :: SourceCharacter but not `\` or LineTerminator | EscapeSequence
请注意,LineContinuations 是不允许的。 JSStr("foo\nbar") 是很好的,但 JSStr("foo\\nbar") 不是。
使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。
type JSStr string
模板是一个来自“text/template(文本/模板)”的专用模板,可以生成安全的HTML文档片段。
type Template struct { // The underlying template's parse tree, updated to be HTML-safe. Tree *parse.Tree // contains filtered or unexported fields}
package mainimport ("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)}}
这里我们演示从目录加载一组模板。
代码:
// 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 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}}`},})// 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.// T0.tmpl is the first name matched, so it becomes the starting template,// the value returned by 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))
此示例演示了共享某些模板并在不同环境中使用它们的一种方法。在这个变体中,我们手动添加多个驱动模板到现有的一组模板。
代码:
// 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{ // 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}}`},})// 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 helpers.templates := template.Must(template.ParseGlob(pattern))// Add one driver template to the bunch; we do this with an explicit template definition._, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")if err != nil { log.Fatal("parsing driver1: ", err)}// Add another driver template._, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")if err != nil { log.Fatal("parsing driver2: ", err)}// We load all the templates before execution. This package does not require// that behavior but html/template's escaping does, so it's a good habit.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)
这里我们演示从不同目录中的文件加载一组模板
代码:
// Here we create different temporary directories and populate them with our sample// template definition files; usually the template files would already// exist in some location known to the program.dir1 := createTestDir([]templateFile{ // T1.tmpl is a plain template file that just invokes T2. {"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},})dir2 := createTestDir([]templateFile{ // T2.tmpl defines a template T2. {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},})// Clean up after the test; another quirk of running as an example.defer func(dirs ...string) { for _, dir := range dirs { os.RemoveAll(dir) }}(dir1, dir2)// Here starts the example proper.// Let's just parse only dir1/T0 and dir2/T2paths := []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)
本示例演示如何将一组驱动程序模板与不同的辅助程序模板集一起使用。
编码:
// 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)}
输出:
T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
func Must(t *Template, err error) *Template
Must 必须是一个帮助函数,如果错误不为零,它会将函数调用返回 (*Template,error) 并发生混乱。它旨在用于变量初始化,如
var t = template.Must(template.New("name").Parse("html"))
func New(name string) *Template
新分配给定名称的新 HTML 模板。
func ParseFiles(filenames ...string) (*Template, error)
ParseFiles 创建一个新的模板并从命名文件中解析模板定义。返回的模板名称将具有第一个文件的(基本)名称和(已解析)内容。必须至少有一个文件。如果发生错误,解析将停止,返回的* Template将为零。
当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。例如,ParseFiles("a/foo", "b/foo") 存储"b/foo" 作为名为“foo”的模板,而“a / foo”不可用。
func ParseGlob(pattern string) (*Template, error)
ParseGlob创建一个新的模板并分析模式标识的文件中的模板定义,该文件必须至少匹配一个文件。返回的模板将具有与该模式匹配的第一个文件的(基本)名称和(已分析)内容。ParseGlob相当于使用模式匹配的文件列表调用ParseFiles。
当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error)
AddParseTree 使用名称和分析树创建一个新模板并将其与 t 关联。
如果 t 或任何关联的模板已经被执行,它会返回一个错误。
func (t *Template) Clone() (*Template, error)
克隆返回模板的副本,包括所有关联的模板。实际表示不会被复制,但是相关模板的名称空间是,因此对副本中的分析进一步调用会将模板添加到副本,但不会添加到原始模板。可以使用克隆来制备通用模板,并在制作克隆后通过添加变体来将它们与其他模板的变体定义一起使用。
如果 t 已经执行,它会返回一个错误。
func (t *Template) DefinedTemplates() string
DefinedTemplates 返回一个字符串,列出定义的模板,以字符串“; defined templates are: ”为前缀。如果没有,则返回空字符串。用于生成错误消息。
func (t *Template) Delims(left, right string) *Template
Delims 将动作分隔符设置为指定的字符串,以便在随后调用 Parse, ParseFiles 或 ParseGlob 时使用。嵌套模板定义将继承这些设置。一个空的分隔符表示相应的默认值:{{或}}。返回值是模板,因此可以将调用链接起来。
func (t *Template) Execute(wr io.Writer, data interface{}) error
执行将解析的模板应用于指定的数据对象,将输出写入 wr 。如果执行模板或写入其输出时发生错误,则执行停止,但部分结果可能已写入输出写入器。模板可以并行安全地执行,但如果并行执行共享 Writer ,输出可能会交错。
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
ExecuteTemplate 将与具有给定名称的 t 关联的模板应用于指定的数据对象,并将输出写入 wr 。如果执行模板或写入其输出时发生错误,则执行停止,但部分结果可能已写入输出写入器。模板可以并行安全地执行,但如果并行执行共享 Writer ,输出可能会交错。
func (t *Template) Funcs(funcMap FuncMap) *Template
Funcs 将参数图的元素添加到模板的功能图中。它必须在解析模板之前调用。如果映射中的值不是具有适当返回类型的函数,则会发生混乱。但是,覆盖地图的元素是合法的。返回值是模板,因此可以将调用链接起来。
func (t *Template) Lookup(name string) *Template
Lookup 返回与 t 关联的给定名称的模板,如果没有这样的模板,则返回 nil。
func (t *Template) Name() string
名称返回模板的名称。
func (t *Template) New(name string) *Template
新分配一个新的 HTML 模板与给定的和相同的分隔符相关联。该关联是可传递的,它允许一个模板通过 {{template}} 动作调用另一个模板。
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 (t *Template) Parse(text string) (*Template, error)
解析将文本解析为t的模板体。文本中的命名模板定义 ({{define ...}} 或 {{block ...}}语句) 定义了与 t 关联的附加模板,并从 t 本身的定义中删除。
在第一次使用“执行”或任何相关模板之前,可以在连续调用 Parse 时重新定义模板。包含仅包含空格和注释的主体的模板定义被视为空白,并且不会替换现有模板的主体。这允许使用 Parse 添加新的命名模板定义而不覆盖主模板主体。
func (t *Template) ParseFiles(filenames ...string) (*Template, error)
ParseFiles 解析命名文件并将生成的模板与 t 关联。如果发生错误,解析将停止并且返回的模板为零;否则是 t 。必须至少有一个文件。
当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。
如果t或任何关联的模板已经执行,则 ParseFiles 将返回一个错误。
func (t *Template) ParseGlob(pattern string) (*Template, error)
ParseGlob 解析由模式标识的文件中的模板定义,并将生成的模板与 t 关联。该模式由 filepath.Glob 处理,并且必须至少匹配一个文件。ParseGlob 相当于使用模式匹配的文件列表调用 t.ParseFiles。
当在不同的目录中分析具有相同名称的多个文件时,提到的最后一个将是结果。
如果 t 或任何关联的模板已被执行,则ParseGlob 将返回错误。
func (t *Template) Templates() []*Template
模板返回与t关联的模板的一部分,包括 t 本身。
URL 封装了已知的安全 URL 或 URL 子字符串(请参阅RFC 3986)。类似于javascript:checkThatFormNotEditedBeforeLeavingPage()
受信任源的 URL 应该放在页面中,但默认情况下动态javascript:
URL 会被过滤掉,因为它们是一个经常被利用的注入向量。
使用这种类型会带来安全风险:封装的内容应该来自可信来源,因为它将逐字包含在模板输出中。
type URL string