В прошлый раз мы рассмотрели, что требуется для запуска сервера, а также узнали, как выводить текстовую информацию на страницу сайта и как возвращать данные в формате JSON. Однако для создания полноценного сайта нам необходимо овладеть основными возможностями Golang в плане работы с HTML. Этим мы и займемся в данной статье.
Статические страницы
Если мы хотим, чтобы при при заходе на стартовую страницу пользователю вместо текстового сообщения открывалась страничка с HTML контентом , то нам достаточно сделать следующее:
В папке “public” создаем новую папку “html”. Затем создаем и размещаем там готовую страницу “startStaticPage.html” .
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Приветствую тебя на стартовой странице этого сайта!</h1>
<ul>
<li>Первый элемент маркированного списка</li>
<li>Второй элемент маркированного списка</li>
<li>Третий элемент маркированного списка</li>
</ul>
</body>
</html>
Переписываем хендлер главной страницы:
package controller
import (
"github.com/julienschmidt/httprouter"
"html/template"
"net/http"
"path/filepath"
)
func StartPage(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
//указываем путь к нужному файлу
path := filepath.Join("public", "html", "startStaticPage.html")
//создаем html-шаблон
tmpl, err := template.ParseFiles(path)
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
//выводим шаблон клиенту в браузер
err = tmpl.Execute(rw, nil)
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
}
Проверяем результат:

При этом необязательно использовать HTML файлы (хотя предпочтительнее и удобнее). Мы можем “сверстать” наш контент прямо в коде в виде обычной строки, а затем превратить его в HTML и отправить клиенту:
func StartPage(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
//верстаем контент страницы в виде обычной строки
content := `<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Приветствую тебя на стартовой странице этого сайта!</h1>
<ul>
<li>Первый элемент маркированного списка</li>
<li>Второй элемент маркированного списка</li>
<li>Третий элемент маркированного списка</li>
<li>Четвертый элемент маркированного списка</li>
</ul>
</body>
</html>`
//создаем html-шаблон
tmpl, err := template.New("example").Parse(content)
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
tmpl.Execute(rw, content)
}
Результат:

Динамические страницы
Если данные подтягиваются из базы и могут меняться, то мы можем генерировать содержание страниц на лету перед выводом. Для этого в Go предусмотрен отдельный пакет, который может соединять шаблоны по именам, а также перебирать в цикле переданные данные, превращая их в элементы интефейса: строки таблиц, блоки, контроллы и т.д.
Создадим страницу “usersDynamicPage.html” в папке “html”:
{{define "users"}}
<!doctype html>
<html lang="ru" class="h-100">
<head>
<title>Title</title>
</head>
<body class="d-flex flex-column h-100">
<table class="table">
<th>id</th>
<th>Имя</th>
<th>Фамилия</th>
{{range .}}
<tr>
<td>{{.Id}}</td>
<td>{{.Name}}</td>
<td>{{.Surname}}</td>
</tr>
{{end}}
</table>
</body>
</html>
{{end}}
Обратите внимание, что здесь используются две специальные конструкции:
- {{define “users”}}{{end}} – объявляется начало и конец именованного шаблона “users”;
- {{range .}} {{end}} – объявляется начало и конец цикла, при помощи которого будут генерироваться строки таблицы на основании массива переданных данных. Переданные данные у нас не имеют ключа, поэтому представлены в виде точки. Однако внутри каждого из элементов массива есть элементы структуры “Id”, “Name” и “Surname”, обращение к которым возможно только по ключу.
Теперь перепишем наш хендлер для вывода списка пользователей:
package controller
import (
"github.com/julienschmidt/httprouter"
"goSiteProject/app/model"
"html/template"
"net/http"
"path/filepath"
)
func GetUsers(rw http.ResponseWriter, r *http.Request, p httprouter.Params) {
//получаем список всех пользователей
users, err := model.GetAllUsers()
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
//указываем путь к файлу с шаблоном
main := filepath.Join("public", "html", "usersDynamicPage.html")
//создаем html-шаблон
tmpl, err := template.ParseFiles(main)
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
//исполняем именованный шаблон "users", передавая туда массив со списком пользователей
err = tmpl.ExecuteTemplate(rw, "users", users)
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
}
Проверяем результат:

К слову, можно использовать условия в виде конструкций {{if}}{{else}}{{end}}, которые можно применять при генерации страниц. С ними вы при желании сможете ознакомиться отдельно, но для примера мы попробуем выводить пользователей, начиная с пятого.
{{range .}}
<tr>
{{if gt .Id 4}}
<td>{{.Id}}</td>
<td>{{.Name}}</td>
<td>{{.Surname}}</td>
{{end}}
</tr>
{{end}}
Результат:

Теперь попробуем собрать одну страницу из нескольких частей.
В папке “public/html” создаем новый файл “common.html” и размещаем там следующий код:
{{define "meta"}}
<meta charset="utf-8">
{{end}}
{{define "styles"}}
<style>
table th {color: red};
</style>
{{end}}
{{define "scripts"}}
<script>
document.addEventListener('DOMContentLoaded', function(){
setTimeout(sayHello, 1000);
});
function sayHello() {
alert("Hello from scripts!");
}
</script>
{{end}}
Далее модифицируем файл “usersDynamicPage.html”. Внутри тега head размещаем ссылки на именованные шаблоны со стилями и метаданными:
<head>
{{template "meta"}}
{{template "styles"}}
<title>Title</title>
</head>
Перед закрывающим тегом body размещаем ссылку на именованный шаблон со скриптами:
{{template "scripts"}}
</body>
Теперь доработаем немного хендлер вывода страницы со списком сотрудников, так, чтобы при генерации мы могли использовать именованные шаблоны и из файла “common.html”.
//указываем пути к файлам с шаблонами
main := filepath.Join("public", "html", "usersDynamicPage.html")
common := filepath.Join("public", "html", "common.html")
//создаем html-шаблон
tmpl, err := template.ParseFiles(main, common)
if err != nil {
http.Error(rw, err.Error(), 400)
return
}
Проверяем результат:

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

Баг. На данной странице не отображается картинка со структурой проекта
Привет! Спасибо за комментарий. Да, картинка куда-то пропала. Попробую восстановить.
Статья писалась почти пять лет назад и за это время WP много раз обновлялся. К сожалению, исходную картинку восстановить не удалось. Но можно посмотреть скриншот структуры проекта из следующего урока и убрать оттуда все, что мы добавляем там. Должно получиться что-то вроде этого:
https://alextonkonogov.ru/wp-content/uploads/2024/07/image-5.png
Будет время, я воспроизведу проект и выложу его на github в отдельных ветках для каждого урока.