Вики это не настоящий вики, если нет возможности редактировать страницы. Давайте создадим два новых обработчика: один с именем editHandler
для отображения формы редактирования страницы, и другой с именем saveHandler
для сохранения данных, введенных через форму.
Сначала мы добавим их в main()
:
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
http.HandleFunc("/save/", saveHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Функция editHandler
загружает страницу (или, если он не существует, создает пустую структуру Page
), и отображает HTML форму.
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
fmt.Fprintf(w, "<h1>Editing %s</h1>"+
"<form action=\"/save/%s\" method=\"POST\">"+
"<textarea name=\"body\">%s</textarea><br>"+
"<input type=\"submit\" value=\"Save\">"+
"</form>",
p.Title, p.Title, p.Body)
}
Эта функция будет хорошо работать, но весь этот жестко закодированный HTML ужасен. Конечно, есть лучший способ.
Пакет html/template
Пакет html/template
является частью стандартной библиотеки Go. Мы можем использовать html/template
, чтобы хранить HTML в отдельном файле, что позволяет нам изменить макет нашей страницы редактирования без изменения основного Go кода.
Во-первых, мы должны добавить html/template
в список импорта. Мы также не будем больше использовать fmt
, поэтому мы должны удалить его.
import (
"html/template"
"io/ioutil"
"net/http"
)
Давайте создадим файл шаблона, содержащий HTML форму. Откройте новый файл с именем edit.html
и добавьте следующие строки:
<h1>Editing {{.Title}}</h1>
<form action="/save/{{.Title}}" method="POST">
<div><textarea name="body" rows="20" cols="80">
{{printf "%s" .Body}}
</textarea></div>
<div><input type="submit" value="Save"></div>
</form>
Измените editHandler
, чтобы использовать шаблон вместо жестко заданного HTML кода:
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
t, _ := template.ParseFiles("edit.html")
t.Execute(w, p)
}
Функция template.ParseFiles
будет читать содержимое edit.html
и возвращать *template.Template
.
Метод t.Execute
выполняет шаблон, записывая сгенерированный HTML для http.ResponseWriter
. Точечные идентификаторы .Title
и .Body
относятся к p.Title
и p.Body
.
Шаблонные директивы заключены в двойные фигурные скобки. Инструкция printf "%s" .Body
является вызовом функции, которая выводит .Body
в виде строки вместо потока байтов, такой же, как вызов fmt.Printf
. Пакет html/template
помогает гарантировать, что только безопасный и правильно выглядящий HTML генерируется действиями шаблона. Например, он автоматически экранирует знак «больше» (>
), заменяя его с помощью >
, чтобы убедиться, что данные пользователя не повреждают HTML форму.
Поскольку сейчас мы работаем с шаблонами, давайте создадим шаблон для нашего viewHandler
называемый view.html
:
<h1>{{.Title}}</h1>
<p>[<a href="/edit/{{.Title}}">edit</a>]</p>
<div>{{printf "%s" .Body}}</div>
Измените viewHandler
соответственно:
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
t, _ := template.ParseFiles("view.html")
t.Execute(w, p)
}
Обратите внимание, что мы использовали почти одинаковый шаблонный код в обоих обработчиках. Давайте удалим это дублирование, переместив шаблонный код в свою собственную функцию:
func renderTemplate(w http.ResponseWriter,
tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}
И измените обработчики для использования этой функции:
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
Если мы закомментируем регистрацию нашего невыполненного обработчика сохранения в main
, мы можем еще раз собрать и протестировать нашу программу. Взглянем на весь код, который мы уже написали.
package main
import (
"html/template"
"io/ioutil"
"log"
"net/http"
)
type Page struct {
Title string
Body []byte
}
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
func loadPage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}
func renderTemplate(w http.ResponseWriter,
tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
//http.HandleFunc("/save/", saveHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Читайте также:
- Веб-приложение на Go: введение
- Веб-приложение на Go: структуры данных, сохранение и загрузка страницы
- Веб-приложение на Go: введение в пакет net/http, пример веб-сервера на Go
- Веб-приложение на Go: использование net/http для обслуживания вики-страниц
- Веб-приложение на Go: обработка несуществующих страниц, сохранение страниц
Комментариев нет:
Отправить комментарий