Давайте немного настроим наше приложение, переместив часть кода обработки ошибок во вспомогательные методы. Это поможет разделить проблемы и избавиться от повторения кода по мере развития программы.
Добавьте новый файл helpers.go
в папку cmd/web
:
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
1 2 |
$ cd $HOME/code/snippetbox $ touch cmd/web/helpers.go |
И добавьте в него следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package main import ( "fmt" "net/http" "runtime/debug" ) // Помощник serverError записывает сообщение об ошибке в errorLog и // затем отправляет пользователю ответ 500 "Внутренняя ошибка сервера". func (app *application) serverError(w http.ResponseWriter, err error) { trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack()) app.errorLog.Println(trace) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } // Помощник clientError отправляет определенный код состояния и соответствующее описание // пользователю. Мы будем использовать это в следующий уроках, чтобы отправлять ответы вроде 400 "Bad // Request", когда есть проблема с пользовательским запросом. func (app *application) clientError(w http.ResponseWriter, status int) { http.Error(w, http.StatusText(status), status) } // Мы также реализуем помощник notFound. Это просто // удобная оболочка вокруг clientError, которая отправляет пользователю ответ "404 Страница не найдена". func (app *application) notFound(w http.ResponseWriter) { app.clientError(w, http.StatusNotFound) } |
Здесь используется совсем немного кода, но вместе с тем вводится несколько новых элементов, которые стоит обсудить:
- В помощнике
serverError()
мы используем функцию debug.Stack(), чтобы получить трассировку стека для текущей горутины и добавить ее в логгер. Возможность видеть полный путь к приложению через трассировку стека может быть полезна при отладке возникнувших ошибок; - В помощнике
clientError()
мы используем функцию http.StatusText() для автоматической генерации понятного человеку текстового представления для кода состояния HTTP. К примеру,http.StatusText(400)
вернет строку"Bad Request"
; - Мы начали использовать специальные константы из пакета
net/http
для кодов состояния HTTP вместо целых чисел. В помощникеserverError()
мы использовали константуhttp.StatusInternalServerError
вместо500
, а в помощникеnotFound()
— константуhttp.StatusNotFound
вместо записи404
.
Использование констант для кодов состояния HTTP является приятной особенностью, которая помогает сделать код понятным и самодокументируемым — особенно при работе с редко используемыми кодами состояния HTTP. Вы можете найти полный список констант кодов состояния по ссылке.
Как только все это будет сделано, вернитесь к файлу handlers.go
и обновите его, для применения функций-помощников из helpers.go
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
package main import ( "fmt" "html/template" "net/http" "strconv" ) func (app *application) home(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { app.notFound(w) // Использование помощника notFound() return } files := []string{ "./ui/html/home.page.tmpl", "./ui/html/base.layout.tmpl", "./ui/html/footer.partial.tmpl", } ts, err := template.ParseFiles(files...) if err != nil { app.serverError(w, err) // Использование помощника serverError() return } err = ts.Execute(w, nil) if err != nil { app.serverError(w, err) // Использование помощника serverError() } } func (app *application) showSnippet(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(r.URL.Query().Get("id")) if err != nil || id < 1 { app.notFound(w) // Использование помощника notFound() return } fmt.Fprintf(w, "Display a specific snippet with ID %d...", id) } func (app *application) createSnippet(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.Header().Set("Allow", http.MethodPost) app.clientError(w, http.StatusMethodNotAllowed) // Используем помощник clientError() return } w.Write([]byte("Создание новой заметки...")) } |
После обновления перезапустите веб-приложение и сделайте HTTP-запрос к http://127.0.0.1:4000 в браузере.
Это все должно привести к возникновению нашей специально оставленной ошибки из прошлого урока, и вы должны увидеть соответствующее сообщение об ошибке и трассировку стека в терминале:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ go run ./cmd/web INFO 2021/01/25 18:04:36 Запуск сервера на :4000 ERROR 2021/01/25 18:04:39 helpers.go:13: open ./ui/html/home.page.tmpl: no such file or directory goroutine 6 [running]: runtime/debug.Stack(0xc00007f200, 0xc00001e2c0, 0x38) /snap/go/6745/src/runtime/debug/stack.go:24 +0x9f main.(*application).serverError(0xc000012c70, 0x827e60, 0xc00014e0e0, 0x8228e0, 0xc00007f200) /home/pc/code/snippetbox/cmd/web/helpers.go:12 +0x65 main.(*application).home(0xc000012c70, 0x827e60, 0xc00014e0e0, 0xc00017e000) /home/pc/code/snippetbox/cmd/web/handlers.go:24 +0x21c net/http.HandlerFunc.ServeHTTP(0xc000012c80, 0x827e60, 0xc00014e0e0, 0xc00017e000) /snap/go/6745/src/net/http/server.go:2042 +0x44 net/http.(*ServeMux).ServeHTTP(0xc000072340, 0x827e60, 0xc00014e0e0, 0xc00017e000) /snap/go/6745/src/net/http/server.go:2417 +0x1ad net/http.serverHandler.ServeHTTP(0xc00014e000, 0x827e60, 0xc00014e0e0, 0xc00017e000) /snap/go/6745/src/net/http/server.go:2843 +0xa3 net/http.(*conn).serve(0xc000117040, 0x8287e0, 0xc000072400) /snap/go/6745/src/net/http/server.go:1925 +0x8ad created by net/http.(*Server).Serve /snap/go/6745/src/net/http/server.go:2969 +0x36c |
Если вы внимательно посмотрите, то заметите небольшую проблему: название файла и номер строки, отображаемые в логе, как helpers.go:13
— это то место, откуда записывается сообщение об ошибке.
Но, зачем нам знать где находится помощник? Мы хотим знать где именно в работе программы возникла ошибка, а не место нахождения помощника который данную ошибку выводит!
В своих логах, мы хотим увидеть только название проблематичного файла и номер строки из трассировки стека, это даст более четкое представление о том, где возникла ошибка.
Это можно сделать, изменив немного код помощника serverError()
чтобы он использовал логгер Output() и установив глубину вызова на 2 (она и так по умолчанию на 2). Снова откройте файл helpers.go
и обновите его следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 |
package main ... func (app *application) serverError(w http.ResponseWriter, err error) { trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack()) app.errorLog.Output(2, trace) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } ... |
Перезапустите веб-приложение из терминала. При повторной попытке перехода на http://127.0.0.1:4000 вы обнаружите, что нам сообщается соответствующее название файла и номер строки (handlers.go:24)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ go run ./cmd/web INFO 2021/01/25 18:17:06 Запуск сервера на :4000 ERROR 2021/01/25 18:21:01 handlers.go:24: open ./ui/html/home.page.tmpl: no such file or directory goroutine 6 [running]: runtime/debug.Stack(0xc00007f200, 0xc00001e2c0, 0x38) /snap/go/6745/src/runtime/debug/stack.go:24 +0x9f main.(*application).serverError(0xc000012c70, 0x827e40, 0xc00014e0e0, 0x822900, 0xc00007f200) /home/pc/code/snippetbox/cmd/web/helpers.go:12 +0x65 main.(*application).home(0xc000012c70, 0x827e40, 0xc00014e0e0, 0xc00017e000) /home/pc/code/snippetbox/cmd/web/handlers.go:24 +0x21c net/http.HandlerFunc.ServeHTTP(0xc000012c80, 0x827e40, 0xc00014e0e0, 0xc00017e000) /snap/go/6745/src/net/http/server.go:2042 +0x44 net/http.(*ServeMux).ServeHTTP(0xc000072340, 0x827e40, 0xc00014e0e0, 0xc00017e000) /snap/go/6745/src/net/http/server.go:2417 +0x1ad net/http.serverHandler.ServeHTTP(0xc00014e000, 0x827e40, 0xc00014e0e0, 0xc00017e000) /snap/go/6745/src/net/http/server.go:2843 +0xa3 net/http.(*conn).serve(0xc000116fa0, 0x8287c0, 0xc000072400) /snap/go/6745/src/net/http/server.go:1925 +0x8ad created by net/http.(*Server).Serve /snap/go/6745/src/net/http/server.go:2969 +0x36c |
Исправляем ошибку из прошлого урока
На данный момент нам больше не нужна искусственная ошибка. Избавиться от нее можно следующий образом:
1 2 |
$ cd $HOME/code/snippetbox $ mv ui/html/home.page.bak ui/html/home.page.tmpl |
Если через терминал у вас не получается, то просто зайдите в папку html
, найдите там файл home.page.bak
и замените расширение .bak
на .tmpl
и всё!
Скачать готовый код
В конце каждого урока мы предоставляем готовый код веб-приложения который соответствует текущей статьи.
Скачать: snippetbox-13.zip
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»