Ранее рассматривалось, как в Go отправлять статические файла, в частности, html-страницы. Определение контента в виде html-страниц довольно удобно: мы используем преимущества html+css+javascript, отделяем представление от основной логики, которая пишется на Go. Однако статические страницы малополезны, когда нам необходимо динамически генерировать некоторый контент на основании различных факторов, например, параметров, переданных через строку запроса. И в этом случае мы можем воспользоваться шаблонами.
Язык Go предоставляет функциональность шаблонов по умолчанию в виде пакета html/template.
Используем протейший шаблон:
package main import ( "fmt" "net/http" "html/template" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { data := "Go Template" tmpl, _ := template.New("data").Parse("<h1>{{ .}}</h1>") tmpl.Execute(w, data) }) fmt.Println("Server is listening...") http.ListenAndServe(":8181", nil) }
С помощью функции template.New("data")
определяется имя шаблона. Затем для установки самого шаблона используется функция
Parse("<h1>{{ .}}</h1>")
. В данном случае шаблон фактически представляет заголовок h1. Но ключевым элементом здесь является
двойная пара фигурных скобок {{ .}}
. Они позволяют вводить в разметку html различные данные. Здесь в качестве данных указана точка.
Точка указывает на контекст шаблона - то есть все данные, которые переданы шаблону.
Стоит отметить, что функция Parse возвращает два значения: собственно шаблон (в данном случае переменная tmpl) и объект ошибки (при ее возникновении). В данном случае объект ошибки не используется, поэтому вместо него идет прочерк.
Чтобы передать шаблону данные, сгенерировать итоговую html-разметку и отправить ее в ответ на запрос, применяется функция Execute:
tmpl.Execute(w, data)
В данном случае переменная data представляет строку, и это как раз те данные, которые будут вставляться в шаблон вместо точки {{ .}}
.
Ну а первый параметр - это объект http.ResponseWriter
, через который отправляются данные.
В итоге при обращении к приложению мы увидим следующий результат:
Шаблон может принимать более сложные данные, которые описываются структурой. Например:
package main import ( "fmt" "net/http" "html/template" ) type ViewData struct{ Title string Message string } func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { data := ViewData{ Title: "World Cup", Message: "FIFA will never regret it", } tmpl := template.Must(template.New("data").Parse(`<div> <h1>{{ .Title}}</h1> <p>{{ .Message}}</p> </div>`)) tmpl.Execute(w, data) }) fmt.Println("Server is listening...") http.ListenAndServe(":8181", nil) }
Здесь данные, передаваемые в шаблон, описываются структурой ViewData, и данная структура будет представлять контекст шаблона. Поэтому чтобы обратиться
к отдельным ее переменным, надо после точки указать название переменной: {{ .Title}}
.
Стоит отметить, что названия переменных следует определять с большой буквы.
Так как в данном случае используются сложные данные, то их надо обернуть в функцию template.Must()
. Сам код шаблона можно переносить на
несколько строк, в этом случае код помещается в косые кавычки. Если код шаблона размещается на одной строке, то можно использовать обычные кавычки.
Результат работы программы:
Однако определение шаблона внутри кода на Go - нелучший вариант, особенно когда шаблон содержит много сложной html-разметки, вкрапления стилей и скриптов javascript. Поэтому более оптимально определять шаблоны в виде отдельных файлов. Например, определим в проекте папку templates, а в ней создадим файл index.html.
Определим в index.html следующий код:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>{{ .Title}}</title> </head> <body> <h1>{{ .Title}}</h1> <p>{{ .Message}}</p> </body> </html>
Используем этот шаблон в коде сервера:
package main import ( "fmt" "net/http" "html/template" ) type ViewData struct{ Title string Message string } func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { data := ViewData{ Title: "World Cup", Message: "FIFA will never regret it", } tmpl, _ := template.ParseFiles("templates/index.html") tmpl.Execute(w, data) }) fmt.Println("Server is listening...") http.ListenAndServe(":8181", nil) }
Для получения кода из файла применяется функция template.ParseFiles(), которой передается путь к файлу. Итоговый результат будет почти таким же, как и в предыдущим случае: