cpp

Веб-сервер на Go. Шаблоны

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

import "html/template"

Для загрузки шаблона из файла предназначена функция ParseFiles() из пакета html/template. Формат функции:

template.ParseFiles(filenames ...string) (*template.Template, error)

Передать данные в загруженный шаблон позволяет метод Execute(). Формат метода:

(*template.Template).Execute(wr io.Writer, data interface{}) error

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

{{ .Название_поля }}

Например, подставим значение поля Name:

{{ .Name }}

Давайте в файл C:\book\index.html добавим шаблон, приведенный в листинге 20.8.

Листинг 20.8. Содержимое файла C:\book\index.html

<!doctype html>
<html lang="ru">
<head>
   <meta charset="utf-8">
   <title>{{ .Title }}</title>
</head>
<body>
 <h1>{{ .H1 }}</h1>
 <p>{{ .Msg }}</p>
 <ul>
  {{range .Users}}
  <li>Имя: {{ .Name }}, возраст: {{ .Age }}</li>
  {{end}}
 </ul>
 {{if .Test}}
  <p>Test = true</p>
 {{else}}
  <p>Test = false</p>
 {{end}}
</body>
</html>

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

{{range .Users}}
<li>Имя: {{ .Name }}, возраст: {{ .Age }}</li>
{{end}}

а также условные инструкции:

{{if .Test}}
 <p>Test = true</p>
{{else}}
 <p>Test = false</p>
{{end}}

Теперь загрузим шаблон и подставим в него значения (листинг 20.9).

Листинг 20.9. Загрузка шаблона из файла

package main

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

type Data struct {
   Title string
   H1    string
   Msg   string
   Users []User
   Test  bool
}
type User struct {
   Name string
   Age  int
}

func main() {
   http.HandleFunc("/",
      func(rw http.ResponseWriter, r *http.Request) {
         rw.Header().Add("Server", "MyServer 1.0")
         data := Data{
            Title: "Заголовок страницы <title>",
            H1:    "Текст заголовка <h1>",
            Msg:   "Текст содержимого <p>",
            Users: []User{
               {Name: "Николай", Age: 25},
               {Name: "Сергей", Age: 40},
               {Name: "<b>Василий</b>", Age: 16},
            },
            Test: true,
         }
         tmpl, err := template.ParseFiles(`C:\book\index.html`)
         if err != nil {
            rw.Header()["Date"] = nil
            http.Error(rw, err.Error(), 404)
            return
         }
         err = tmpl.Execute(rw, data)
         if err != nil {
            rw.Header()["Date"] = nil
            http.Error(rw, err.Error(), 404)
         }
      })
   fmt.Println(http.ListenAndServe(":3000", nil))
}

Открываем веб-браузер и в адресной строке вводим: http://localhost:3000/. Если мы отобразим исходный HTML-код, то увидим код, приведенный в листинге 20.10.

Листинг 20.10. HTML-код после подстановки значений в шаблон

<!doctype html>
<html lang="ru">
<head>
   <meta charset="utf-8">
   <title>Заголовок страницы &lt;title&gt;</title>
</head>
<body>
 <h1>Текст заголовка &lt;h1&gt;</h1>
 <p>Текст содержимого &lt;p&gt;</p>
 <ul>
  
  <li>Имя: Николай, возраст: 25</li>
  
  <li>Имя: Сергей, возраст: 40</li>
  
  <li>Имя: &lt;b&gt;Василий&lt;/b&gt;, возраст: 16</li>
  
 </ul>

  <p>Test = true</p>

</body>
</html>

Обратите внимание: после подстановки все специальные символы были заменены HTML-эквивалентами:

<h1>Текст заголовка &lt;h1&gt;</h1>

Если в шаблон нужно подставлять HTML-код, то вместо пакета html/template нужно использовать пакет text/template. Функционал у пакетов одинаковый, поэтому в листинге 20.9 достаточно заменить инструкцию:

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

следующим кодом:

import (
   "fmt"
   "net/http"
   "text/template"
)

Перезапускаем сервер и обновляем страницу http://localhost:3000/. Если мы отобразим исходный HTML-код, то увидим код, приведенный в листинге 20.11.

Листинг 20.11. HTML-код после подстановки значений в шаблон

<!doctype html>
<html lang="ru">
<head>
   <meta charset="utf-8">
   <title>Заголовок страницы <title></title>
</head>
<body>
 <h1>Текст заголовка <h1></h1>
 <p>Текст содержимого <p></p>
 <ul>
  
  <li>Имя: Николай, возраст: 25</li>
  
  <li>Имя: Сергей, возраст: 40</li>
  
  <li>Имя: <b>Василий</b>, возраст: 16</li>
  
 </ul>

  <p>Test = true</p>

</body>
</html>

В результате данные были подставлены как есть, без какой-либо замены специальных символов:

<h1>Текст заголовка <h1></h1>

В результате мы получили не валидный HTML-код. Помните, что при использовании пакета text/template заботиться о замене специальных символов HTML-эквивалентами нужно самостоятельно, иначе можно получить не валидный HTML-код или зловредный код на языке JavaScript, который открывает диалоговое окно в бесконечном цикле или ворует конфиденциальные данные. Не подставляйте в текстовый шаблон данные, полученные из неизвестного источника. Пользуйтесь в этом случае функционалом пакета html/template.

Примечание

Учебник Go (Golang)
Учебник Go (Golang) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов

cpp