Статьи

Изучение движка шаблонов Go

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

Шаблонный движок Go, как и большинство шаблонных движков, лежит где-то вдоль спектра между логикой и встроенной логикой. В веб-приложении обработчик обычно запускает механизм шаблонов. Следующая диаграмма показывает, как механизм шаблонов Go вызывается из обработчика. Обработчик вызывает механизм шаблонов, передавая ему шаблон (ы) для использования, обычно в виде списка файлов шаблонов и динамических данных. Затем шаблонизатор генерирует HTML-код и записывает его в ResponseWriter, который добавляет его в ответ HTTP, отправляемый обратно клиенту.

Шаблоны Go — это текстовые документы (для веб-приложений это, как правило, HTML-файлы), в которые встроены определенные команды, называемые действиями. В движке шаблонов Go шаблон представляет собой текст (обычно в файле шаблона), в который встроены действия, которые анализируются и выполняются механизмом шаблонов для создания другого результирующего фрагмента текста. Go имеет стандартную библиотеку текста / шаблонов, которая является общим механизмом шаблонов для любого типа текстового формата, и библиотеку html / шаблонов, которая является специальным механизмом шаблонов для HTML. Действия добавляются между двумя двойными скобками ( {{) и (}} ). Вот пример того, как выглядит очень простой шаблон:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ . }}
  </body>
</html>

Этот шаблон помещается в файл шаблона с именем tmpl.html.
Естественно, мы можем иметь столько файлов шаблонов, сколько захотим. Файлы шаблонов должны быть в читаемом текстовом формате, но могут иметь любое расширение, но в этом случае, поскольку он генерирует вывод HTML, я использовал расширение .html.

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

Использование движка веб-шаблонов Go требует двух шагов:
  1. Разбор источника шаблона в текстовом формате, который может быть строкой или файлом шаблона, для создания проанализированной структуры шаблона.
  2. Выполните проанализированный шаблон, передав ему ResponseWriter и некоторые данные. Это запускает механизм шаблонов, чтобы объединить проанализированный шаблон с данными для генерации окончательного HTML-кода, который передается в ResponseWriter.

Давайте посмотрим на конкретный простой пример:
package main
import (
  "net/http"
  "html/template"
)
func process(w http.ResponseWriter, r *http.Request) {  
  t, _ := template.ParseFiles("tmpl.html")
  t.Execute(w, "Hello World!")
}
func main() {
  server := http.Server{
    Addr: "127.0.0.1:8080",
  }
  http.HandleFunc("/process", process)
  server.ListenAndServe()
}

Мы снова вернулись на наш сервер.
На этот раз у нас есть функция-обработчик с именем process, которая запускает механизм шаблонов. Сначала мы анализируем файл шаблона tmpl.html, используя функцию ParseFiles. Функция возвращает проанализированный шаблон типа Template и ошибку, которую мы для удобства игнорируем.
t, _ := template.ParseFiles("tmpl.html")

Затем мы вызываем метод Execute для применения данных (в данном случае, строки Hello World!) К шаблону. 
t.Execute(w, "Hello World!")

Мы передаем ResponseWriter вместе с данными, чтобы сгенерированный HTML-код мог быть передан обратно клиенту.
Когда вы запускаете этот пример, файл шаблона должен находиться в том же каталоге, что и двоичный файл (помните, что мы не указали абсолютный путь к файлу).

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

Разбор шаблонов


ParseFiles — это отдельная функция, которая анализирует файлы шаблонов и создает проанализированную структуру шаблона, которую можно выполнить позже.
Однако функция ParseFiles на самом деле является вспомогательной функцией метода ParseFiles в структуре Template. По сути, когда вы вызываете ParseFiles, Go создает новый шаблон с именем файла в качестве имени шаблона, а затем вызывает ParseFiles для этого шаблона.

Другими словами, эти два:
t, _ := template.ParseFiles("tmpl.html")

и
t := template.New("tmpl.html")
t, _ := t.ParseFiles("tmpl.html")

ParseFiles может принимать одно или несколько имен файлов в качестве параметров (это переменная функция, то есть функция, которая может принимать переменное число параметров).
Однако он по-прежнему возвращает только один шаблон, независимо от количества переданных файлов. Что с этим?

Когда мы передаем более одного файла, возвращенный разобранный шаблон имеет имя и содержимое первого файла.
Остальные файлы анализируются как карта шаблонов, к которой можно обратиться позже во время выполнения. Вы можете думать об этом как о ParseFiles, возвращающем шаблон, когда вы предоставляете один файл, и набор шаблонов, когда вы предоставляете более одного файла. Это важно, когда мы рассмотрим включение шаблона в шаблон или последующее вложение шаблонов.

Другой способ разбора файлов — использовать функцию ParseGlob, которая использует сопоставление с шаблоном вместо конкретных файлов.
Используя тот же пример:
t, _ := template.ParseFiles("tmpl.html")

и
t, _ := template.ParseGlob("*.html")

было бы то же самое, если бы tmpl.html был единственным файлом по тому же пути.

Анализ файлов, вероятно, является наиболее распространенным, но вы также можете анализировать шаблоны, используя строки.
Фактически все другие способы синтаксического анализа шаблонов в конечном итоге вызывают метод Parse для анализа шаблона. Используя тот же пример снова:
t, _ := template.ParseFiles("tmpl.html")

и
  tmpl := `<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    {{ . }}
  </body>
</html>
`
  t := template.New("tmpl.html")
  t, _ = t.Parse(tmpl)
  t.Execute(w, "Hello World!")

также одинаковы, при условии, что tmpl.html содержит тот же HTML. 

До сих пор мы игнорировали ошибку, которая возвращается вместе с проанализированным шаблоном.
Обычная практика Go — это, конечно, обработка ошибки, но Go предоставляет еще один механизм для обработки ошибок, возвращаемых при разборе шаблонов.
t  := template.Must(template.ParseFiles("tmpl.html"))
The Must function wraps around a function that returns a pointer to a template and an error, and panics if the error is not a nil. 

Executing templates

The usual way to execute a template is to call the Execute function on a template, passing it the ResponseWriter and the data. This works well when the parsed template is a single template instead of a template set. If you call the Execute method on a template set, it’ll simply take the first template in the set. However if you want to execute a different template in the template set and not the first one, you need to use the ExecuteTemplate method. For example, take this code:
t, _ := template.ParseFiles("t1.html", "t2.html")
This contains two templates, the first is named t1.html and the second is t2.html (the name of the template is the name of the file, extension and all, unless you change it). If we call the Execute method on it:
t.Execute(w, "Hello World!")
It will result in t1.html being executed. If you want to execute t2.html, you need to do this instead:
t.ExecuteTemplate(w, "t2.html", "Hello World!")
This article took you through how to trigger the template engine to parse and execute templates. My book, 
Go Web Programming, explores Go’s template engine even more.