Template parsing error template 1 unclosed action

Introduction This article has a relation with the previous article about getting the IP address of a running docker container. […]

Introduction

This article has a relation with the previous article about getting the IP address of a running docker container. That article exist in this link with the title of ‘How to get IP Address of a Running Docker Container’. Actually, upon executing a command to get IP address of a running docker container, the error as exist in the title of this article appear. The error exist upon executing the following command as follows :

C:UsersAdministratorDownloads>docker inspect --format '{{ .NetworkSettings.IPAddress }}' mycontainer
Template parsing error: template: :1: unclosed action

C:UsersAdministratorDownloads>

Apparently, although the format or the syntax of the command for getting the IP address is correct, it still generate an error message. So, this article will discuss on how to solve the error and get the IP address of the running docker container. Moreover, the docker container is running in a host using Microsoft Windows as its operating system.

Solution

Basically, the error has a very simple solution. After searching through Google to find out the reason. It actually exist in this link. One of the comment available on that link which is a stackoverflow post is giving the answer. Actually, in Microsoft Windows, the format parameter’s value cannot use a single quote.  It must use double quote to enclose the value of the format parameter. On the other hand, in other operating system, it is the right syntax to use single quote. Since the command run in a host using Microsoft Windows as its operating system, just revise the above command and execute it once more as follows :

C:UsersAdministratorDownloads>docker inspect --format "{{ .NetworkSettings.IPAddress }}" mycontainer
172.17.0.3

C:UsersAdministratorDownloads>

It is actually exist in this link which available as the previous article. It is an article with the title of ‘How to get IP Address of a Running Docker Container’. As in the above command execution, after revising the command by using double quote, the result appear. It is giving the IP address of the running docker container.

Hello,

I have a very strange error on one machine. docker inspect —format {{json …} fails but docker inspect works.

# docker inspect --format "{{json .Config.Labels}}" 0d0dc58585ff
Template parsing error: template: :1: unclosed action

# docker inspect --format "{{.Config.Labels}}" 0d0dc58585ff
map[]

I have the same errors on image or containers, with or without labels present.

# docker inspect --format "{{.Config.Labels}}" 0f6fe6506ec2
map[wl.docker-builder.user:a512753 wl.source.changeset:5f0edffb82bf38361ed12917dccf9e7e198f8f04 wl.source.url:https://gitlab.xxxxxxx/docker/docker-builder.git wl.docker-builder.secret:xxxxxxxxxxxx]

# docker inspect --format "{{json .Config.Labels}}" 0f6fe6506ec2
Template parsing error: template: :1: unclosed action

I have an another server with the same Docker version, but without this issue.

When I look at the logs, no query are sent to the Docker Server with using format JSON:

# docker inspect --format "{{.Config.Labels}}" 0d0dc58585ff
Nov 18 09:17:58 xxxxx docker: time="2015-11-18T09:17:58.154997705+01:00" level=info msg="GET /v1.20/containers/0d0dc58585ff/json"
map[]
# docker inspect --format "{{json .Config.Labels}}" 0d0dc58585ff
Template parsing error: template: :1: unclosed action

Please find below some information on my machine:

uname -a
Linux xxxxxx 3.10.0-229.7.2.el7.x86_64 #1 SMP Tue Jun 23 22:06:11 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
# docker version
Client:
 Version:      1.8.2
 API version:  1.20
 Package Version: docker-1.8.2-7.el7.centos.x86_64
 Go version:   go1.4.2
 Git commit:   bb472f0/1.8.2
 Built:
 OS/Arch:      linux/amd64

Server:
 Version:      1.8.2
 API version:  1.20
 Package Version:
 Go version:   go1.4.2
 Git commit:   bb472f0/1.8.2
 Built:
 OS/Arch:      linux/amd64
# docker info
Containers: 2
Images: 29
Storage Driver: devicemapper
 Pool Name: docker-253:7-524-pool
 Pool Blocksize: 65.54 kB
 Backing Filesystem: extfs
 Data file: /dev/loop0
 Metadata file: /dev/loop1
 Data Space Used: 1.576 GB
 Data Space Total: 107.4 GB
 Data Space Available: 56.27 GB
 Metadata Space Used: 2.249 MB
 Metadata Space Total: 2.147 GB
 Metadata Space Available: 2.145 GB
 Udev Sync Supported: true
 Deferred Removal Enabled: false
 Data loop file: /var/lib/docker/devicemapper/devicemapper/data
 Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
 Library Version: 1.02.93-RHEL7 (2015-01-28)
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.10.0-229.7.2.el7.x86_64
Operating System: CentOS Linux 7 (Core)
CPUs: 4
Total Memory: 7.798 GiB
Name: xxxx
ID: GLD5:E5TV:HU4Q:LCVP:DK66:WNZ4:F2H6:TF6O:2R66:TDDU:L56Z:WBEC

I am running Docker on a virtual machine (VmWare)

Overview

Text is all around us as software developers. Code is text, HTML is text, XNL/JSON/YAML/TOML is text, Markdown is text, CSV is text. All these text formats are designed to cater to both humans and machines. Humans should be able to read and edit textual formats with plain text editors. 

But there are many cases where you need to generate text in a certain format. You may convert from one format to another, create your own DSL, generate some helper code automatically, or just customize an email with user-specific information. Whatever the need is, Go is more than able to assist you along the way with its powerful templates. 

In this tutorial, you’ll learn about the ins and outs of Go templates and how to use them for powerful text generation.

What Are Go Templates?

Go templates are objects that manage some text with special placeholders called actions, which are enclosed by double curly braces: {{ some action }}. When you execute the template, you provide it with a Go struct that has the data the placeholders need. 

Here’s a quick example that generates knock knock jokes. A knock knock joke has a very strict format. The only things that change are the identity of the knocker and the punchline.

package main

import (
    "text/template"
    "os"
)


type Joke struct {
    Who string
    Punchline string
}

func main() {
    t := template.New("Knock Knock Joke")
    text := `Knock KnocknWho's there?
             {{.Who}}
             {{.Who}} who?
             {{.Punchline}}
            `
    t.Parse(text)

    jokes := []Joke{
        {"Etch", "Bless you!"},
        {"Cow goes", "No, cow goes moo!"},
    }

    for _, joke := range jokes {
        t.Execute(os.Stdout, joke)
    }
}

Output:

Knock Knock
Who's there?
Etch
Etch who?
Bless you!

Knock Knock
Who's there?
Cow goes
Cow goes who?
No, cow goes moo!

Understanding Template Actions

The template syntax is very powerful, and it supports actions such as data accessors, functions, pipelines, variables, conditionals, and loops.

Data Accessors

Data accessors are very simple. They just pull data out of the struct starting. They can drill into nested structs too:

func main() {
    family := Family{
        Father: Person{"Tarzan"},
        Mother: Person{"Jane"},
        ChildrenCount: 2,
    }
    
    t := template.New("Father")
    text := "The father's name is {{.Father.Name}}"
    t.Parse(text)
    t.Execute(os.Stdout, family)
}

If the data is not a struct, you can use just {{.}} to access the value directly:

func main() {   
    t := template.New("")
    t.Parse("Anything goes: {{.}}n")
    t.Execute(os.Stdout, 1)
    t.Execute(os.Stdout, "two")
    t.Execute(os.Stdout, 3.0)
    t.Execute(os.Stdout, map[string]int{"four": 4})
}

Output:

Anything goes: 1
Anything goes: two
Anything goes: 3
Anything goes: map[four:4]

We will see later how to deal with arrays, slices, and maps.

Functions

Functions really elevate what you can do with templates. There are many global functions, and you can even add template-specific functions. The complete list of global functions is available on the Go website.

Here is an example of how to use the printf function in a template:

func main() {
    t := template.New("")
    t.Parse(`Keeping just 2 decimals of π: {{printf "%.2f" .}}
                  `)
    t.Execute(os.Stdout, math.Pi)
}

Output:

Keeping just 2 decimals of π: 3.14

Pipelines

Pipelines let you apply multiple functions to the current value. Combining different functions significantly expands the ways in which you can slice and dice your values. 

In the following code, I chain three functions. First, the call function executes the function pass to Execute(). Then the len function returns the length of the result of the input function, which is 3 in this case. Finally, the printf function prints the number of items.

func main() {
    t := template.New("")
    t.Parse(`{{ call . | len | printf "%d items" }}
                  `)
    t.Execute(os.Stdout, func() string { return "abc" })
}

Output:

3 items

Variables

Sometimes you want to reuse the result of a complex pipeline multiple times. With Go templates, you can define a variable and reuse it as many times as you want. The following example extracts the first and last name from the input struct, quotes them, and stores them in the variables $F and $L. Then it renders them in normal and reverse order. 

Another neat trick here is that I pass an anonymous struct to the template to make the code more concise and avoid cluttering it with types that are used only in one place.

func main() {
    t := template.New("")
    t.Parse(`{{ $F := .FirstName | printf "%q"}}
             {{ $L := .LastName  | printf "%q"}}
             Normal:  {{$F}} {{$L}}
             Reverse: {{$L}} {{$F}}`
    )
    t.Execute(os.Stdout, struct {
        FirstName string
        LastName  string
    }{
        "Gigi",
        "Sayfan",
    })
}

Output:

Normal:  "Gigi" "Sayfan"
Reverse: "Sayfan" "Gigi"

Conditionals

But let’s not stop here. You can even have conditions in your templates. There is an if-end action and if-else-end action. The if clause is displayed if the output of the conditional pipeline is not empty:

func main() {
    t := template.New("")
    t.Parse(`{{ if . -}} {{ . }} {{ else }} 
             No data is available {{ end }}`
    )
    t.Execute(os.Stdout, "42")
    t.Execute(os.Stdout, "")
}

Output:

42 
            No data is available 

Note that the else clause causes a new line, and the “No data available” text is significantly indented.

Loops

Go templates have loops too. This is super useful when your data contains slices, maps, or other iterables. The data object for a loop can be any iterable Go object like array, slice, map, or channel. The range function allows you to iterate over the data object and create an output for each element. Let’s see how to iterate over a map:

func main() {
    t := template.New("")
    e := `Name,Scores
          {{range $k, $v := .}}{{$k}}
          {{range $s := $v}},{{$s}}{{end}}
          {{end}}
          `
    t.Parse(e)
    t.Execute(os.Stdout, map[string][]int{
        "Mike":  {88, 77, 99},
        "Betty": {54, 96, 78},
        "Jake":  {89, 67, 93},
    })
}

Output:

Name,Scores
          Betty,54,96,78
          Jake,89,67,93
          Mike,88,77,99

As you can see, the leading whitespace is still a problem. I wasn’t able to find a decent way to address it within the template syntax. It will require post-processing. In theory, you can place a dash to trim whitespace preceding or following actions, but it doesn’t work in the presence of range.

Text templates are implemented in the text/template package. In addition to everything we’ve seen so far, this package can also load templates from files and compose multiple templates using the template action. The Template object itself has many methods to support such advanced use cases:

  • ParseFiles()
  • ParseGlob()
  • AddParseTree()
  • Clone()
  • DefinedTemplates()
  • Delims()
  • ExecuteTemplate()
  • Funcs()
  • Lookup()
  • Option()
  • Templates()

Due to space limitations, I will not go into further detail (maybe in another tutorial).

HTML Templates 

HTML templates are defined in the html/template package. It has exactly the same interface as the text template package, but it is designed to generate HTML that is safe from code injection. This is done by carefully sanitizing the data before embedding it in the template. The working assumption is that template authors are trusted, but the data provided to the template can’t be trusted. 

This is important. If you automatically apply templates you receive from untrusted sources then the html/template package will not protect you. It is your responsibility to check the templates.

Let’s see the difference between the output of text/template and html/template. When using the text/template, it’s easy to inject JavaScript code into the generated output.

package main

import (
    "text/template"
    "os"
)

func main() {
    t, _ := template.New("").Parse("Hello, {{.}}!")
    d := ""
    t.Execute(os.Stdout, d)
}

Output:

Hello, !

But importing the html/template instead of text/template prevents this attack, by escaping the script tags and the parentheses:

Hello, <script>alert('pwened!')</script>!

Dealing With Errors

There are two types of errors: parsing errors and execution errors. The Parse() function parses the template text and returns an error, which I ignored in the code samples, but in production code you want to catch these errors early and address them. 

If you want a quick and dirty exit then the Must() method takes the output of a method that returns (*Template, error)—like Clone(), Parse(), or ParseFiles()—and panics if the error is not nil. Here is how you check for an explicit parsing error:

func main() {
    e := "I'm a bad template, }}{{"
    _, err := template.New("").Parse(e)
    if err != nil {
        msg := "Failed to parsing: '%s'.nError: %vn"
        fmt.Printf(msg, e, err)
    }
}

Output:

Failed to parse template: 'I'm a bad template, }}{{'.
Error: template: :1: unexpected unclosed action in command

Using Must() just panics if something is wrong with the template:

func main() {
    e := "I'm a bad template, }}{{"
    template.Must(template.New("").Parse(e))
}

Output:

panic: template: :1: unexpected unclosed action in command

The other kind of error is an execution error if the provided data doesn’t match the template. Again, you can check explicitly or use Must() to panic. I recommend in this case that you check and have a recovery mechanism in place. 

Usually, there is no need to bring the whole system down just because an input doesn’t meet the requirements. In the following example, the template expects a field called Name on the data struct, but I provide a struct with a field called FullName.

func main() {
    e := "There must be a name: {{.Name}}"
    t, _ := template.New("").Parse(e)
    err := t.Execute(
        os.Stdout,
        struct{ FullName string }{"Gigi Sayfan"},
    )
    if err != nil {
        fmt.Println("Fail to execute.", err)
    }
}

Output:

There must be a name: Fail to execute. 
template: :1:24: executing "" at <.Name>:
can't evaluate field Name in type struct { FullName string }

Conclusion

Go has a powerful and sophisticated templating system. It is used to great effect in many large projects like Kubernetes and Hugo. The html/template package provides a secure, industrial-strength facility to sanitize the output of web-based systems. In this tutorial, we covered all the basics and some intermediate use cases. 

There are still more advanced features in the template packages waiting to be unlocked. Play with templates and incorporate them into your programs. You will be pleasantly surprised how concise and readable your text-generation code looks.

I’m trying to assign container names to an array.

The command works perfectly when I’m running it without assigning to an array:

ARR=docker network inspect --format '{{ range $key, $value := .Containers }}{{ printf "%sn" $value.Name}}{{ end }}' some_network $$ echo $ARR

I’d like to do same thing but over SSH:

ssh root@68.183.74.135 "
ARR=( $(docker network inspect --format '"'{{ range $key, $value := .Containers }}{{ printf "%sn" $value.Name}}{{ end }}'"' some_network) ) && echo $ARR
"

but it complains:

Template parsing error: template: :1: unexpected unclosed action in command

I guess the reason lies in string interpolation or escaping ', can u give any advice guys?

AdminBee's user avatar

AdminBee

20.8k18 gold badges47 silver badges69 bronze badges

asked Sep 13, 2021 at 11:48

maslak's user avatar

3

Within double-quotes, the command substitution would be handled on the local end, and the local shell would parse the quoting inside.

If you want to run it on the remote, you need something like

ssh root@68.183.74.135 '
  ARR=( $(docker network inspect --format '''{{ range $key, $value := .Containers }}{{ printf "%sn" $value.Name}}{{ end }}''' some_network) ) && echo $ARR
'

or you could maybe avoid the quoting hell by giving the commands via stdin instead:

ssh root@68.183.74.135 <<'EOF'

ARR=( $(docker network inspect --format '{{ range $key, $value := .Containers }}{{ printf "%sn" $value.Name}}{{ end }}' some_network) ) && echo $ARR
EOF

or by having the whole script in a separate file (on the remote or local side), and running from that.

glenn jackman's user avatar

glenn jackman

81.9k14 gold badges114 silver badges164 bronze badges

answered Sep 13, 2021 at 12:05

ilkkachu's user avatar

ilkkachuilkkachu

126k15 gold badges227 silver badges377 bronze badges

0

обзор

Текст вокруг нас как разработчиков программного обеспечения. Код – это текст, HTML – это текст, XNL / JSON / YAML / TOML – это текст, Markdown – это текст, CSV – это текст. Все эти текстовые форматы предназначены для людей и машин. Люди должны иметь возможность читать и редактировать текстовые форматы с помощью текстовых редакторов.

Но во многих случаях вам нужно генерировать текст в определенном формате. Вы можете конвертировать из одного формата в другой, создать свой собственный DSL, автоматически сгенерировать некоторый вспомогательный код или просто настроить электронное письмо с информацией для конкретного пользователя. Независимо от необходимости, Go более чем способен помочь вам на этом пути с его мощными шаблонами.

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

Что такое шаблоны Go?

Шаблоны Go – это объекты, которые управляют некоторым текстом с помощью специальных заполнителей, называемых действиями, которые заключены в двойные фигурные скобки: {{ some action }} . Когда вы выполняете шаблон, вы предоставляете ему структуру Go, которая содержит данные, необходимые для заполнителей.

Вот быстрый пример, который генерирует анекдоты о стуке. Шутка тук-тук имеет очень строгий формат. Единственными вещами, которые меняются, является личность молотка и изюминка.

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

package main

import (

    “text/template”

    “os”

)

type Joke struct {

    Who string

    Punchline string

}

func main() {

    t := template.New(“Knock Knock Joke”)

    text := `Knock KnocknWho’s there?

             {{.Who}}

             {{.Who}} who?

             {{.Punchline}}

            `

    t.Parse(text)

    jokes := []Joke{

        {“Etch”, “Bless you!”},

        {“Cow goes”, “No, cow goes moo!”},

    }

    for _, joke := range jokes {

        t.Execute(os.Stdout, joke)

    }

}

Output:

Knock Knock

Who’s there?

Etch

Etch who?

Bless you!

Knock Knock

Who’s there?

Cow goes

Cow goes who?

No, cow goes moo!

Понимание действий шаблона

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

Средства доступа к данным

Средства доступа к данным очень просты. Они просто извлекают данные из начала структуры. Они также могут углубляться во вложенные структуры:

01

02

03

04

05

06

07

08

09

10

11

12

func main() {

    family := Family{

        Father: Person{“Tarzan”},

        Mother: Person{“Jane”},

        ChildrenCount: 2,

    }

    t := template.New(“Father”)

    text := “The father’s name is {{.Father.Name}}”

    t.Parse(text)

    t.Execute(os.Stdout, family)

}

Если данные не являются структурой, вы можете использовать просто {{.}} Для прямого доступа к значению:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

func main() {

    t := template.New(“”)

    t.Parse(“Anything goes: {{.}}n”)

    t.Execute(os.Stdout, 1)

    t.Execute(os.Stdout, “two”)

    t.Execute(os.Stdout, 3.0)

    t.Execute(os.Stdout, map[string]int{“four”: 4})

}

Output:

Anything goes: 1

Anything goes: two

Anything goes: 3

Anything goes: map[four:4]

Позже мы увидим, как обращаться с массивами, срезами и картами.

функции

Функции действительно улучшают то, что вы можете делать с шаблонами. Существует множество глобальных функций, и вы даже можете добавить функции, специфичные для шаблона. Полный список глобальных функций доступен на веб-сайте Go .

Вот пример того, как использовать функцию printf в шаблоне:

01

02

03

04

05

06

07

08

09

10

func main() {

    t := template.New(“”)

    t.Parse(`Keeping just 2 decimals of π: {{printf “%.2f” .}}

                  `)

    t.Execute(os.Stdout, math.Pi)

}

Output:

Keeping just 2 decimals of π: 3.14

Трубопроводы

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

В следующем коде я объединяю три функции. Сначала функция вызова выполняет передачу функции в Execute() . Затем функция len возвращает длину результата входной функции, которая в данном случае равна 3. Наконец, функция printf печатает количество элементов.

01

02

03

04

05

06

07

08

09

10

func main() {

    t := template.New(“”)

    t.Parse(`{{ call . | len | printf “%d items” }}

                  `)

    t.Execute(os.Stdout, func() string { return “abc” })

}

Output:

3 items

переменные

Иногда вы хотите повторно использовать результат сложного конвейера несколько раз. С помощью шаблонов Go вы можете определить переменную и использовать ее столько раз, сколько захотите. В следующем примере извлекаются имя и фамилия из входной структуры, заключаются в кавычки и сохраняются в переменных $F и $L Затем он отображает их в обычном и обратном порядке.

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

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

func main() {

    t := template.New(“”)

    t.Parse(`{{ $F := .FirstName | printf “%q”}}

             {{ $L := .LastName |

             Normal: {{$F}} {{$L}}

             Reverse: {{$L}} {{$F}}`

    )

    t.Execute(os.Stdout, struct {

        FirstName string

        LastName string

    }{

        “Gigi”,

        “Sayfan”,

    })

}

Output:

Normal: “Gigi” “Sayfan”

Reverse: “Sayfan” “Gigi”

Conditionals

Но давайте не будем останавливаться на достигнутом. Вы даже можете иметь условия в своих шаблонах. Существует действие if-end действие if-else-end . Предложение if отображается, если выходные данные условного конвейера не пусты:

01

02

03

04

05

06

07

08

09

10

11

12

13

func main() {

    t := template.New(“”)

    t.Parse(`{{ if . -}} {{ . }} {{ else }}

             No data is available {{ end }}`

    )

    t.Execute(os.Stdout, “42”)

    t.Execute(os.Stdout, “”)

}

Output:

42

            No data is available

Обратите внимание, что предложение else вызывает новую строку, а текст «Нет данных» имеет значительный отступ.

Loops

В шаблонах Go тоже есть петли. Это очень полезно, когда ваши данные содержат фрагменты, карты или другие итерации. Объектом данных для цикла может быть любой итеративный объект Go, например массив, фрагмент, карта или канал. Функция range позволяет перебирать объект данных и создавать выходные данные для каждого элемента. Давайте посмотрим, как перебрать карту:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

func main() {

    t := template.New(“”)

    e := `Name,Scores

          {{range $k, $v := .}}{{$k}}

          {{range $s := $v}},{{$s}}{{end}}

          {{end}}

          `

    t.Parse(e)

    t.Execute(os.Stdout, map[string][]int{

        “Mike”: {88, 77, 99},

        “Betty”: {54, 96, 78},

        “Jake”: {89, 67, 93},

    })

}

Output:

Name,Scores

          Betty,54,96,78

          Jake,89,67,93

          Mike,88,77,99

Как вы можете видеть, ведущий пробел все еще является проблемой. Я не смог найти достойного способа решения этой проблемы в синтаксисе шаблона. Это потребует постобработки. Теоретически, вы можете поместить тире, чтобы обрезать пропуски перед или после действий, но это не работает при наличии range .

Текстовые шаблоны

Текстовые шаблоны реализованы в пакете text / template . В дополнение ко всему, что мы видели до сих пор, этот пакет может также загружать шаблоны из файлов и создавать несколько шаблонов, используя действие шаблона. Сам объект Template имеет много методов для поддержки таких расширенных вариантов использования:

  • ParseFiles ()
  • ParseGlob ()
  • AddParseTree ()
  • Клон ()
  • DefinedTemplates ()
  • Delims ()
  • ExecuteTemplate ()
  • Funcs ()
  • Погляди()
  • Вариант ()
  • Шаблоны ()

Из-за нехватки места я не буду вдаваться в подробности (возможно, в другой урок).

HTML-шаблоны

Шаблоны HTML определены в пакете html / template . Он имеет тот же интерфейс, что и пакет текстового шаблона, но он предназначен для генерации HTML, который защищен от внедрения кода. Это делается путем тщательной очистки данных перед их внедрением в шаблон. Рабочим предположением является то, что авторам шаблонов доверяют, но данные, предоставленные шаблону, нельзя доверять.

Это важно. Если вы автоматически применяете шаблоны, полученные из ненадежных источников, пакет html / template вас не защитит. Вы несете ответственность за проверку шаблонов.

Давайте посмотрим на разницу между выводом text/template и html/template . При использовании текста / шаблона легко внедрить код JavaScript в сгенерированный вывод.

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

package main

import (

    “text/template”

    “os”

)

func main() {

    t, _ := template.New(“”).Parse(“Hello, {{.}}!”)

    d := “<script>alert(‘pawned!’)</script>”

    t.Execute(os.Stdout, d)

}

Output:

Hello, <script>alert(‘pawned!’)</script>!

Но импорт html/template вместо text/template предотвращает эту атаку, избегая тегов скрипта и скобок:

1

Hello, &lt;script&gt;alert('pwened!')&lt;/script&gt;!

Работа с ошибками

Есть два типа ошибок: ошибки синтаксического анализа и ошибки выполнения. Функция Parse() анализирует текст шаблона и возвращает ошибку, которую я игнорировал в примерах кода, но в рабочем коде вы хотите отлавливать эти ошибки заранее и исправлять их.

Если вам нужен быстрый и грязный выход, то метод Must() принимает выходные данные метода, который возвращает (*Template, error) как Clone() , Parse() или ParseFiles() – и паникует, если ошибка не ноль. Вот как вы проверяете явную ошибку разбора:

01

02

03

04

05

06

07

08

09

10

11

12

13

func main() {

    e := “I’m a bad template, }}{{“

    _, err := template.New(“”).Parse(e)

    if err != nil {

        msg := “Failed to parsing: ‘%s’.nError: %vn”

        fmt.Printf(msg, e, err)

    }

}

Output:

Failed to parse template: ‘I’m a bad template, }}{{‘.

Error: template: :1: unexpected unclosed action in command

Использование Must() просто паникует, если что-то не так с шаблоном:

1

2

3

4

5

6

7

8

func main() {

    e := “I’m a bad template, }}{{“

    template.Must(template.New(“”).Parse(e))

}

Output:

panic: template: :1: unexpected unclosed action in command

Другой вид ошибки – ошибка выполнения, если предоставленные данные не соответствуют шаблону. Опять же, вы можете проверить явно или использовать Must() для паники. В этом случае я рекомендую проверить и установить механизм восстановления.

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

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

func main() {

    e := “There must be a name: {{.Name}}”

    t, _ := template.New(“”).Parse(e)

    err := t.Execute(

        os.Stdout,

        struct{ FullName string }{“Gigi Sayfan”},

    )

    if err != nil {

        fmt.Println(“Fail to execute.”, err)

    }

}

Output:

There must be a name: Fail to execute.

template: :1:24: executing “” at <.Name>:

can’t evaluate field Name in type struct { FullName string }

Вывод

Go имеет мощную и сложную систему шаблонов. Он используется с большим эффектом во многих крупных проектах, таких как Kubernetes и Hugo. Пакет html / template обеспечивает безопасное, промышленное средство для дезинфекции результатов веб-систем. В этом уроке мы рассмотрели все основы и некоторые промежуточные варианты использования.

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

Recommend Projects

  • React photo

    React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo

    Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo

    Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo

    TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo

    Django

    The Web framework for perfectionists with deadlines.

  • Laravel photo

    Laravel

    A PHP framework for web artisans

  • D3 photo

    D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Visualization

    Some thing interesting about visualization, use data art

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo

    Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo

    Microsoft

    Open source projects and samples from Microsoft.

  • Google photo

    Google

    Google ❤️ Open Source for everyone.

  • Alibaba photo

    Alibaba

    Alibaba Open Source for everyone

  • D3 photo

    D3

    Data-Driven Documents codes.

  • Tencent photo

    Tencent

    China tencent open source team.

Понравилась статья? Поделить с друзьями:
  • Template error while templating string expected token name
  • Template error while templating string expected token end of print statement got
  • Telnet сбой подключения как исправить
  • Telltale file validation failed как исправить
  • Teknoblackops update error checking for updates