Как использовать шаблоны в Go

    Команда Simple-Server
    03.06.2026
    13 мин

    Материал подготовлен командой Simple-Server для администраторов VPS и выделенных серверов. Команды и пути проверяйте на тестовой машине перед production.

    Кратко о задаче

    Используемый шаблон, как правило, привязывается к определенной структуре данных (например, struct), данные из которой будут появляться внутри шаблона.

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

    Это фрагменты текста, заключенные в фигурные скобки {{ }}, в которых выполняется вычисление или подстановка некоторых данных. Именно за счет них текст внутри шаблона по своему наполнению становится динамическим.

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

    К условиям относятся классические конструкции if-else, которые используются непосредственно внутри шаблона. Благодаря условиям можно добавлять или убирать из конечного вывода целые текстовые блоки, что существенно увеличивает возможности шаблонизации и гибкость генерации контента.

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

    Непосредственно в самом Go есть 3 наиболее часто используемые функции для управления шаблонами:

    • New. Создает новый шаблон, который впоследствии нужно будет определить.
    • Parse``. Анализирует переданную строку, содержащую текст шаблона, после чего возвращает уже готовый к использованию шаблон.
    • Execute. Выполняет готовый шаблон (который прошел этап парсинга), применяя к нему указанную структуру данных, после чего записывает результат в заданную переменную

    Кстати, еще существует функция ParseFiles для обработки не строки с содержимым шаблона, а целых файлов.

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

    package main // помимо шаблонизатора подключаем os, который предоставляет независимый от платформы интерфейс для взаимодействия с операционной системой. В нашем случае он по понадобиться для вывода результата выполнения шаблона в консоль import ( "os" "text/template" ) // определяем структуру, данные которой будут подставляться в шаблон type Person struct { Name string Age int } func main() { some_person := Person{"Alex", 32} // экземпляр ранее определенной структуры some_template := "This is {{ .Name }} and he is {{ .Age }} years old" // текстовое содержимое самого шаблона с проставленными внутри действиями (actions), заключенными в фигурные скобки // создаем новый шаблон и парсим его содержимое, тем самым подготавливая его к дальнейшему использованию ready_template, err := template.New("test").Parse(some_template) // проверяем на наличие ошибок (nil означает ничего, как null в C) if err != nil { panic(err) // останавливаем выполнение и выводим содержимое ошибки } // выполняем шаблон и выводим его в консоль приложения err = ready_template.Execute(os.Stdout, some_person) // ВЫВОД: This is Alex and he is 32 years old // снова проверяем на наличие ошибок if err != nil { panic(err) // останавливаем выполнение и выводим содержимое ошибки } }

    «Скомпилированный» с помощью функции Parse шаблон можно использовать повторно, но уже с данными из другой структуры. Например, вы могли бы продолжить функцию main из кода выше:

    // здесь идет ранее написанный код ... another_person := Person{"Maks", 27} // создаем другой экземпляр структуры err = ready_template.Execute(os.Stdout, another_person) }

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

    This is {{ .Name }} and he is {{ .Age }} years old

    Кстати, внутри шаблона можно напрямую обратиться к данным, которые были переданы при выполнении:

    package main import ( "os" "text/template" ) func main() { some_template := "Тут обошлось {{ . }}" ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, "без данных, просто текст") // ВЫВОД: Тут обошлось без данных, просто текст }

    Особенности синтаксиса шаблонов

    В самом трививальном случае шаблон может просто выводить статичный текст не используя никаких дополнительных данных:

    import ( "os" "text/template" ) ... some_template := "Просто обычный текст" ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, ”без данных”) // ВЫВОД: Просто обычный текст

    Статический текст внутри «Действия» (фигурных скобок)

    Обычный статический текст можно усложнить дополнительными данными:

    import ( "os" "text/template" ) ... some_template := "Не совсем обычный текст с {{ \“дополнительными\” }} данными" // не забываем также экранировать двойные кавычки ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, ”без данных”) // ВЫВОД: Не совсем обычный текст с дополнительными данными

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

    ... some_template := "Не совсем обычный текст с {{- \“дополнительными\” -}} данными" ... ready_template.Execute(os.Stdout, ”без данных”) // ВЫВОД: Не совсем обычный текст сдополнительнымиданными // вывод выше не опечатка — были убраны пробелы перед и после слова “дополнительными”

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

    В отличие от текста, который нужно указывать в кавычках, числа автоматически подставляются в вывод:

    ... some_template := "Возможно этот код писало {{ 5 }} человек. Но это не точно..." ... ready_template.Execute(os.Stdout, ”без данных”) // ВЫВОД: Возможно этот код писало 5 человек. Но это не точно...

    Как и с текстом, с числами тоже можно использовать маркеры обрезки:

    ... some_template := "Возможно этот код писало {{- 5 }} человек. Но это не точно..." ... ready_template.Execute(os.Stdout, ”без данных”) // ВЫВОД: Возможно этот код писало5 человек. Но это не точно...

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

    Как и в самом Go, переменная сначала определяется с указанием имени и значения, а уже потом используется. Определение внутренней переменной происходит с помощью знака доллара:

    package main import ( "os" "text/template" ) func main() { some_template := "Сначала определяем переменную {{- $some_variable :=`Привет, я переменная` }}, а потом используем ее: \"{{ $some_variable }}\"" ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, ”без данных”) // ВЫВОД: Сначала определяем переменную, а потом используем ее: "Привет, я переменная" }

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

    В шаблонах Go есть возможность ветвления на основе какой либо логики за счет классических для всех языков программирования условных операторов if / else:

    package main import ( "os" "text/template" ) func main() { some_template := "{{ if eq . `hello` -}} Привет! {{ else -}} Пока! {{ end }}" // после каждого условия используем маркер обрезки, чтобы убрать пробел вначале вывода ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, "hello") // ВЫВОД: Привет! }

    В этом примере используется функция eq (equal), которая сравнивает переданное в шаблон значение (к которому мы обращаемся через точку) со строкой hello.

    Также обратите внимание, что каждое ветвление завершается оператором end.

    На самом деле можно опустить сравнение строк, а сразу передавать булеву переменную, делая код лаконичнее:

    package main import ( "os" "text/template" ) func main() { some_template := "{{ if . -}} Привет! {{ else -}} Пока! {{ end }}" ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, false) // ВЫВОД: Пока! }

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

    package main import ( "os" "text/template" ) func main() { some_list := []string{ "First", "Second", "Third" }  some_template := "Посчитаем по порядку: {{ range .}}{{.}}, {{ end }}" ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, some_list) // ВЫВОД: Посчитаем по порядку:  First, Second, Third, }

    В этом примере есть одна проблема — последний элемент списка выводит после себя лишнюю запятую с пробелом. Поэтому код выше можно модернизировать, добавив соответствующую проверку — если элемент последний в списке, то запятая с пробелом не ставится:

    package main import ( "os" "text/template" ) func main() { some_list := []string{ "First", "Second", "Third" }  some_template := "Посчитаем по порядку: {{ range $index, $element := .}}{{ if $index }}, {{ end }}{{$element}}{{ end }}" ready_template, err := template.New("test").Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, some_list) // ВЫВОД: Посчитаем по порядку: First, Second, Third }

    В этом примере добавляются две новые переменные, которые обновляются каждую итерацию цикла — индекс и значение элемента.

    Перед каждым элементом мы выводим запятую с пробелом, но только в том случае, когда индекс ($index) не равен нулю.

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

    Однако, перед непосредственным использованием их нужно предварительно объявить и задекларировать в шаблоне:

    package main import ( "os" "text/template" ) func manipulate(first_arg, second_arg int) int {  return first_arg + second_arg }  func main() { some_list := []int{1, 2, 3 }  some_template := "Сложения индекса и элемента по порядку: {{ range $index, $element := .}}{{ if $index }}, {{ end }}{{$index}} + {{$element}} = {{ do_manipulation $index $element }}{{ end }}" ready_template, err := template.New("test").Funcs(template.FuncMap{"do_manipulation": manipulate}).Parse(some_template) if err != nil { panic(err) } ready_template.Execute(os.Stdout, some_list) // ВЫВОД: Сложения индекса и элемента по порядку: 0 + 1 = 1, 1 + 2 = 3, 2 + 3 = 5 }

    В этом примере мы нарочно переименовали Go-функцию manipulate внутри шаблона в do_manipulation — если функционал языка позволяет, то почему бы это не сделать?

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

    ready_template, err := template.New("test").Funcs(template.FuncMap{"manipulate": manipulate}).Parse(some_template)

    Работа с HTML-шаблонами в Go

    Как уже было сказано ранее, в Go есть дополнительный пакет для работы с шаблонами на HTML — html/template.

    Использование этого пакета, в отличие от стандартного text/template, защищает приложения от атак межсайтового скриптинга (XSS), поскольку Go предотвращает ввод каких-либо данных во время рендеринга.

    Соответственно, импорт пакетов немного видоизменяется:

    import ( "html/template" "net/http" )

    Пакет net/http потребуется для запуска HTTP-сервера на локальной машине, который необходим для теста дальнейшего примера.

    Файл шаблона с HTML-разметкой

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

    Кстати, файл назовем классическим именем — index.html:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1>{{ .Tittle }}</h1> <p> {{ .Text }} </p> </body> </html>

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

    Теперь напишем минимальный Golang-код, запускающий HTTP-сервер и отправляющий результат выполнения шаблона в качестве ответа на любые запросы к серверу:


    Нужен сервер для практики? Арендуйте VPS/VDS в России — root-доступ, NVMe, DDoS-защита и поддержка 24/7.

    VPS для проекта

    VPS с root-доступом, NVMe и поддержкой 24/7 на Simple-Server.

    StarterVDS

    490₽

    в месяц

    1 ядро

    1 ГБ RAM

    20 ГБ NVMe

    • 1 IPv4
    • KVM
    • Root-доступ
    • Безлимитный трафик
    Заказать VPS
    Рекомендуем

    PerformanceVDS

    1190₽

    в месяц

    2 ядра

    4 ГБ RAM

    60 ГБ NVMe

    • 1 IPv4
    • KVM
    • Root-доступ
    • Базовая DDoS-защита
    Заказать VPS

    Нужна другая конфигурация или чистый VPS без панели?

    Все тарифы VPS

    Похожие статьи, которые могут быть вам интересны