Spec-Zone.ru › Go

Пакет template

  • import "html/template"
  • Обзор
  • Индекс
  • Примеры

Обзор

Пакет template (html/template) реализует шаблоны, управляемые данными, для генерации HTML-вывода, защищенного от внедрения кода. Он предоставляет тот же интерфейс, что и text/template, и должен использоваться вместо text/template всякий раз, когда вывод представляет собой HTML.

В данном руководстве основное внимание уделено функциям безопасности пакета. Сведения о том, как программировать шаблоны, см. в документации для text/template.

Введение

Этот пакет оборачивает text/template, позволяя использовать его API для шаблонов для безопасного анализа и выполнения HTML-шаблонов.

tmpl, err := template.New("name").Parse(...)
// Error checking elided
err = tmpl.Execute(out, data)

Если операция выполнена успешно, tmpl теперь защищен от внедрения кода. В противном случае err — это ошибка, определенная в документации для ErrorCode.

HTML-шаблоны обрабатывают значения данных как обычный текст, который должен быть закодирован, чтобы его можно было безопасно встроить в HTML-документ. Экранирование контекстно-зависимое, поэтому действия могут появляться в контекстах JavaScript, CSS и URI.

Модель безопасности, используемая этим пакетом, предполагает, что авторы шаблонов являются доверенными лицами, в то время как параметр data функции 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 — псевдонимы внутренних функций экранирования.

Для этих внутренних функций экранирования, если этап обработки возвращает значение nil, оно обрабатывается как пустая строка.

Пространства имён и атрибуты data-

Атрибуты с пространством имён обрабатываются так, как будто у них нет пространства имён. Учитывая фрагмент

<a my:href="{{.}}"></a>

Во время разбора атрибут будет обрабатываться как простой "href". Таким образом, шаблон во время разбора становится:

<a my:href="{{. | urlescaper | attrescaper}}"></a>

Аналогично атрибутам с именованными пространствами, атрибуты с префиксом "data-" обрабатываются так, как будто у них нет префикса "data-". Таким образом, учитывая

<a data-href="{{.}}"></a>

Во время разбора это становится

<a data-href="{{. | urlescaper | attrescaper}}"></a>

Если атрибут имеет и пространство имён, и префикс "data-", только пространство имён будет удалено при определении контекста. Например,

<a my:data-href="{{.}}"></a>

Это обрабатывается так, как будто "my:data-href" — это просто "data-href", а не "href", как это было бы, если бы префикс "data-" также игнорировался. Таким образом, во время разбора это становится просто

<a my:data-href="{{. | attrescaper}}"></a>

В качестве особого случая, атрибуты с пространством имён "xmlns" всегда обрабатываются как содержащие URL. Учитывая фрагменты

<a xmlns:title="{{.}}"></a>
<a xmlns:href="{{.}}"></a>
<a xmlns:onclick="{{.}}"></a>

Во время разбора они становятся:

<a xmlns:title="{{. | urlescaper | attrescaper}}"></a>
<a xmlns:href="{{. | urlescaper | attrescaper}}"></a>
<a xmlns:onclick="{{. | urlescaper | attrescaper}}"></a>

Ошибки

См. документацию ErrorCode для получения подробной информации.

Более полная картина

Остальную часть комментария к этому пакету можно пропустить при первом чтении; он содержит подробности, необходимые для понимания контекстов экранирования и сообщений об ошибках. Большинству пользователей не нужно понимать эти детали.

Контексты

Предполагая, что {{.}} — это `O'Reilly: Как <i>вы</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>

См. файл package.json, чтобы понять, как данные, не являющиеся строками, маршалируются для встраивания в контексты JavaScript.

Типизированные строки

По умолчанию этот пакет предполагает, что все этапы обработки генерируют простую текстовую строку. Он добавляет этапы обработки экранирования, необходимые для правильного и безопасного встраивания этой текстовой строки в соответствующий контекст.

Когда значение данных не является обычным текстом, вы можете убедиться, что оно не экранируется избыточно, пометив его типом.

Типы HTML, JS, URL и другие из content.go могут содержать безопасный контент, исключенный из экранирования.

Шаблон

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 определяет "безопасный" в соответствии с этим пакетом.

Этот пакет предполагает, что авторы шаблонов являются доверенными лицами, что параметр data функции Execute недоверен, и стремится сохранить следующие свойства при использовании недоверенных данных:

Свойство сохранения структуры: "... когда автор шаблона записывает HTML-тег в безопасном языке шаблонов, браузер интерпретирует соответствующую часть вывода как тег независимо от значений недоверенных данных, и аналогично для других структур, таких как границы атрибутов и границы строк JS и CSS."

Свойство эффекта кода: "... должен выполняться только код, указанный автором шаблона, в результате вставки вывода шаблона в страницу, и должен выполняться весь код, указанный автором шаблона."

Свойство наименьшего удивления: "Разработчик (или рецензент кода), знакомый с HTML, CSS и JavaScript, знающий, что происходит контекстно-зависимое автоматическое экранирование, должен быть в состоянии посмотреть на {{.}} и правильно предположить, какое экранирование происходит."

Ранее литералы шаблонов ECMAScript 6 отключались по умолчанию и могли быть включены с помощью переменной среды GODEBUG=jstmpllitinterp=1. Теперь литералы шаблонов поддерживаются по умолчанию, и установка jstmpllitinterp не оказывает никакого влияния.

Пример

Код:

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)

Вывод:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>My page</title>
	</head>
	<body>
		<div>My photos</div><div>My blog</div>
	</body>
</html>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>My another page</title>
	</head>
	<body>
		<div><strong>no rows</strong></div>
	</body>
</html>

Пример (Автоэкранирование)

Код:

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)

Вывод:

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

Пример (Экранирование)

Код:

const s = `"Fran & Freddie's Diner" <tasty@example.com>`
v := []any{`"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...))

Вывод:

&#34;Fran &amp; Freddie&#39;s Diner&#34; &lt;tasty@example.com&gt;
&#34;Fran &amp; Freddie&#39;s Diner&#34; &lt;tasty@example.com&gt;
&#34;Fran &amp; Freddie&#39;s Diner&#34;32&lt;tasty@example.com&gt;
\"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
\"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
\"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E
%22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E

Индекс

  • func HTMLEscape(w io.Writer, b []byte)
  • func HTMLEscapeString(s string) string
  • func HTMLEscaper(args ...any) string
  • func IsTrue(val any) (truth, ok bool)
  • func JSEscape(w io.Writer, b []byte)
  • func JSEscapeString(s string) string
  • func JSEscaper(args ...any) string
  • func URLQueryEscaper(args ...any) string
  • тип CSS
  • тип Error
  • func (e *Error) Error() string
  • тип ErrorCode
  • тип FuncMap
  • тип HTML
  • тип HTMLAttr
  • тип JS
  • тип JSStr
  • тип Srcset
  • тип Template
  • func Must(t *Template, err error) *Template
  • func New(name string) *Template
  • func ParseFS(fs fs.FS, patterns ...string) (*Template, error)
  • 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 any) error
  • func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) 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) ParseFS(fs fs.FS, patterns ...string) (*Template, error)
  • func (t *Template) ParseFiles(filenames ...string) (*Template, error)
  • func (t *Template) ParseGlob(pattern string) (*Template, error)
  • func (t *Template) Templates() []*Template
  • тип URL

Примеры

Пакет
Template.Delims
Шаблон (Блок)
Шаблон (Glob)
Шаблон (Вспомогательные функции)
Шаблон (Parsefiles)
Шаблон (Share)
Пакет (Автоэкранирование)
Пакет (Экранирование)

Файлы пакета

attr.go attr_string.go content.go context.go css.go delim_string.go doc.go element_string.go error.go escape.go html.go js.go jsctx_string.go state_string.go template.go transition.go url.go urlpart_string.go

func HTMLEscape

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

HTMLEscape записывает в w экранированный HTML-эквивалент данных b.

func HTMLEscapeString

func HTMLEscapeString(s string) string

HTMLEscapeString возвращает экранированный HTML-эквивалент данных s.

func HTMLEscaper

func HTMLEscaper(args ...any) string

HTMLEscaper возвращает HTML-эквивалент, закодированный по правилам HTML, текстового представления своих аргументов.

func IsTrue 1.6

func IsTrue(val any) (truth, ok bool)

IsTrue сообщает, является ли значение 'true', в смысле не нулевым для своего типа, и имеет ли значение осмысленное значение истинности. Это определение истинности, используемое в операторах if и подобных им.

func JSEscape

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

JSEscape записывает в w JavaScript-эквивалент, закодированный по правилам JavaScript, обычных текстовых данных b.

func JSEscapeString

func JSEscapeString(s string) string

JSEscapeString возвращает JavaScript-эквивалент, закодированный по правилам JavaScript, обычных текстовых данных s.

func JSEscaper

func JSEscaper(args ...any) string

JSEscaper возвращает JavaScript-эквивалент, закодированный по правилам JavaScript, текстового представления своих аргументов.

func URLQueryEscaper

func URLQueryEscaper(args ...any) string

URLQueryEscaper возвращает закодированное значение текстового представления своих аргументов, подходящее для встраивания в URL-запрос.

type CSS

CSS инкапсулирует известное безопасное содержимое, соответствующее любому из следующих:

  1. Производство CSS3 таблицы стилей, например, `p { color: purple }`.
  2. Производство CSS3 правила, например, `a[href=~"https:"].foo#bar`.
  3. Производства CSS3 деклараций, например, `color: red; margin: 2px`.
  4. Производство CSS3 значения, например, `rgba(0, 0, 255, 127)`.

См. https://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

Error описывает проблему, возникшую во время обработки шаблона.

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 // Go 1.4
    // 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 (*Error) Error

func (e *Error) Error() string

type ErrorCode

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 it
from 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

    // ErrJSTemplate: "... appears in a JS template literal"
    // Example:
    //     <script>var tmpl = `{{.Interp}}`</script>
    // Discussion:
    //   Package html/template does not support actions inside of JS template
    //   literals.
    //
    // Deprecated: ErrJSTemplate is no longer returned when an action is present
    // in a JS template literal. Actions inside of JS template literals are now
    // escaped as expected.
    ErrJSTemplate
)

type FuncMap

type FuncMap = template.FuncMap

type 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 с помощью json.Unmarshal, а затем передача полученного объекта в шаблон, где он будет преобразован в очищенный JSON при представлении в контексте JavaScript.

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 Srcset 1.10

Srcset инкапсулирует известный безопасный атрибут srcset (см. https://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset).

Использование этого типа представляет собой риск безопасности: инкапсулируемое содержимое должно поступать из надёжного источника, так как оно будет включено в вывод шаблона дословно.

type Srcset string

type Template

Template — специализированный шаблон из "text/template", который генерирует фрагмент безопасного HTML-документа.

type Template struct {

    // The underlying template's parse tree, updated to be HTML-safe.
    Tree *parse.Tree // Go 1.2
    // contains filtered or unexported fields
}

Пример (Блок)

Код:

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

Вывод:

Names:
- Gamora
- Groot
- Nebula
- Rocket
- Star-Lord
Names: Gamora, Groot, Nebula, Rocket, Star-Lord

Пример (Шаблон по образцу)

Здесь мы демонстрируем загрузку набора шаблонов из каталога.

Код:

// 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/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)

Пример (Общий доступ)

В этом примере показано, как использовать одну группу шаблонов-драйверов с различными наборами вспомогательных шаблонов.

Код:

// 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

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

Must — вспомогательная функция, которая оборачивает вызов функции, возвращающей (*Template, ошибка), и вызывает панику, если ошибка не равна null. Предназначена для использования при инициализации переменных, таких как

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

func New

func New(name string) *Template

New создаёт новый HTML-шаблон с заданным именем.

func ParseFS 1.16

func ParseFS(fs fs.FS, patterns ...string) (*Template, error)

ParseFS подобен ParseFiles или ParseGlob, но читает из файловой системы fs вместо файловой системы хоста. Принимает список шаблонов поиска файлов. (Обратите внимание, что большинство имён файлов служат шаблонами поиска файлов, соответствующими только самим себе.)

func ParseFiles

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

ParseFiles создаёт новый Template и парсит определения шаблонов из указанных файлов. Имя возвращённого шаблона будет иметь имя (базовое) и содержимое (разбор) первого файла. Должен быть хотя бы один файл. Если произошла ошибка, парсинг прекращается, и возвращаемый *Template равен null.

При парсинге нескольких файлов с одинаковым именем в разных каталогах последним указанный файл будет тем, который будет результатом. Например, ParseFiles("a/foo", "b/foo") сохранит "b/foo" в качестве шаблона с именем "foo", а "a/foo" недоступен.

func ParseGlob

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

ParseGlob создаёт новый Template и парсит определения шаблонов из файлов, указанных шаблоном. Файлы соответствуют семантике filepath.Match, и шаблон должен соответствовать хотя бы одному файлу. Возвращаемый шаблон будет иметь имя (базовое) и содержимое (разбор) первого файла, соответствующего шаблону. 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)

Clone возвращает дубликат шаблона, включая все связанные шаблоны. Фактическое представление не копируется, но пространство имён связанных шаблонов копируется, поэтому дальнейшие вызовы Template.Parse в копии добавят шаблоны в копию, но не в оригинал. Template.Clone можно использовать для подготовки общих шаблонов и их использования с вариантами определений для других шаблонов, добавив варианты после создания копии.

Возвращает ошибку, если t уже был выполнен.

func (*Template) DefinedTemplates 1.6

func (t *Template) DefinedTemplates() string

DefinedTemplates возвращает строку, перечисляющую определённые шаблоны, префикс которой — строка "; определенные шаблоны: ". Если их нет, возвращается пустая строка. Используется для создания сообщения об ошибке.

func (*Template) Delims

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

Delims устанавливает разделители действий на указанные строки, которые будут использоваться в последующих вызовах Template.Parse, ParseFiles или ParseGlob. Вложенные определения шаблонов будут унаследовать настройки. Пустой разделитель соответствует соответствующему значению по умолчанию: {{ или }}. Возвращаемое значение — шаблон, поэтому вызовы можно объединять.

Пример

Код:

const text = "<<.Greeting>> {{.Name}}"

data := struct {
    Greeting string
    Name     string
}{
    Greeting: "Hello",
    Name:     "Joe",
}

t := template.Must(template.New("tpl").Delims("<<", ">>").Parse(text))

err := t.Execute(os.Stdout, data)
if err != nil {
    log.Fatal(err)
}

Вывод:

Hello {{.Name}}

func (*Template) Execute

func (t *Template) Execute(wr io.Writer, data any) error

Execute применяет обработанный шаблон к указанному объекту данных, записывая результат в wr. Если при выполнении шаблона или записи его результата возникла ошибка, выполнение останавливается, но частичные результаты могут быть уже записаны в выходной поток. Шаблон можно безопасно выполнять параллельно, хотя если параллельные выполнения используют один и тот же Writer, вывод может быть чередующимся.

func (*Template) ExecuteTemplate

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

ExecuteTemplate применяет шаблон, связанный с t, имеющий данное имя, к указанному объекту данных и записывает вывод в wr. Если при выполнении шаблона или записи его вывода произошла ошибка, выполнение останавливается, но частичные результаты могут быть уже записаны в writer вывода. Шаблон может быть выполнен в параллели, хотя если параллельные выполнения используют один 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

Name возвращает имя шаблона.

func (*Template) New

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

New выделяет новый HTML шаблон, связанный с заданным и имеющий те же разделители. Связь, которая является транзитивной, позволяет одному шаблону вызывать другой с помощью действия {{template}}.

Если шаблон с данным именем уже существует, новый HTML шаблон его заменит. Существующий шаблон будет сброшен и отсоединён от t.

func (*Template) Option 1.5

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

Option устанавливает параметры для шаблона. Параметры описываются строками, либо простой строкой, либо "ключ=значение". В строке параметра может быть не более одного знака равенства. Если строка параметра не распознается или иным образом неверна, Option вызывает панику.

Известные параметры:

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)

Parse разбирает текст как тело шаблона для t. Определения именованных шаблонов ({{define ...}} или {{block ...}} инструкции) в тексте определяют дополнительные шаблоны, связанные с t, и удаляются из определения самого t.

Шаблоны могут быть переопределены в последующих вызовах Parse до первого использования Template.Execute на t или любом связанном шаблоне. Определение шаблона с телом, содержащим только пробелы и комментарии, считается пустым и не заменит тело существующего шаблона. Это позволяет использовать Parse для добавления новых определений именованных шаблонов без перезаписи основного тела шаблона.

func (*Template) ParseFS 1.16

func (t *Template) ParseFS(fs fs.FS, patterns ...string) (*Template, error)

ParseFS подобен Template.ParseFiles или Template.ParseGlob, но читает из файловой системы fs вместо файловой системы хостовой операционной системы. Он принимает список шаблонов совпадений.

func (*Template) ParseFiles

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

ParseFiles разбирает указанные файлы и связывает полученные шаблоны с t. Если возникает ошибка, разбор останавливается, и возвращаемый шаблон равен nil; в противном случае он равен t. Должен быть хотя бы один файл.

При разборе нескольких файлов с одинаковым именем в разных директориях, последним упомянутым будет файл, который получается.

ParseFiles возвращает ошибку, если t или любой связанный шаблон уже был выполнен.

func (*Template) ParseGlob

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

ParseGlob разбирает определения шаблонов в файлах, определённых шаблоном, и связывает получившиеся шаблоны с t. Файлы сопоставляются по семантике filepath.Match, и шаблон должен соответствовать хотя бы одному файлу. ParseGlob эквивалентно вызову t.ParseFiles со списком файлов, соответствующих шаблону.

При разборе нескольких файлов с одинаковым именем в разных директориях, последним упомянутым будет файл, который получается.

ParseGlob возвращает ошибку, если t или любой связанный шаблон уже был выполнен.

func (*Template) Templates

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

Templates возвращает срез шаблонов, связанных с t, включая сам t.

type URL

URL инкапсулирует известный безопасный URL или подстроку URL (см. RFC 3986). URL, такой как `javascript:checkThatFormNotEditedBeforeLeavingPage()` из надёжного источника, должен находиться на странице, но по умолчанию динамические URL `javascript:` отфильтровываются, поскольку они являются часто используемым вектором инъекций.

Использование этого типа представляет собой риск безопасности: инкапсулируемое содержимое должно поступать из надёжного источника, так как оно будет включено в вывод шаблона дословно.

type URL string

© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/html/template/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API