Go: с нуля до своего блога Урок 3 из 50
Первая программа на Go: веб-сервер на net/http за 20 минут
Третий урок курса по Go. Первая программа на Go и работающий веб-сервер: package main, func main, http.HandleFunc, http.ListenAndServe и порт 8080. Откройте localhost:8080 в браузере и увидите ответ своей программы раньше, чем выучите хоть одно правило языка. И что делать, если порт занят.
Зачем это нужно
В прошлом уроке вы смотрели на чужой сервер со стороны клиента. Сегодня встанете на другую сторону: напишете программу, которая отвечает браузеру.
На это уйдёт двадцать минут, и ни одного правила языка учить не придётся. Всё остальное в курсе — подробности к тому, что вы сделаете сейчас.
Сразу целиком
Рабочее место у вас уже собрано в прошлом уроке. Создайте рядом с ним новую папку sabaq-02, откройте её в редакторе (File → Open Folder) и выполните в терминале:
go mod init sabaq02
Теперь создайте в ней файл main.go и положите в него эту программу:
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Привет!"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Перейдите в терминале в эту папку и запустите:
go run .
Курсор замрёт, новая строка не появится. Так и должно быть: программа работает и ждёт. Откройте браузер и наберите localhost:8080.
На экране «Привет!». Это ответила ваша программа.
Остановить её — Ctrl+C в терминале.
Если не получилось. Строка ввода вернулась сразу, а в терминале написано
address already in use— порт 8080 занят другой программой. Замените в коде8080на8081и откройтеlocalhost:8081. Написаноcommand not found: go— терминал не видит Go: закройте и откройте его заново, а если не помогло, вернитесь к прошлому уроку, к проверкеgo version. Windows спросил про доступ в сеть — разрешите для частных сетей; порт:8080без адреса слева означает «все сетевые интерфейсы», то есть в домашней сети сервер увидят и с телефона. Пока вы учитесь, это удобно; чтобы закрыться только своей машиной, пишут127.0.0.1:8080.
Разбор
package main и func main
package main говорит: это не библиотека, а программа, которую можно запустить. func main — то место, откуда запуск начинается. Ровно одна такая функция на программу, и Go ищет её по имени.
Пока примите это как обязательную рамку. К пакетам вернёмся в уроке про пакеты и модули, когда их станет несколько.
import “net/http”
Всё, что нужно для веба, уже есть в самом Go — отдельно ставить нечего. net/http умеет и спрашивать как клиент, и отвечать как сервер. Мы взяли вторую половину.
http.HandleFunc: что отвечать
Первый аргумент — путь, второй — что на нём делать. Мы сказали: на пути "/" выполняй вот эту функцию.
Обратите внимание: функция написана прямо внутри вызова, без имени. В Go так можно, и для коротких обработчиков это обычное дело.
Образ. Список поручений, вывешенный на двери: «придут за справкой — выдайте вот эту». Пока никто не пришёл, список просто висит и ничего не делает.
w и r: ответ и запрос
Два аргумента обработчика — это те самые две стороны из прошлого урока.
r — запрос: что читатель спросил, каким методом, по какому пути, с какими заголовками. Сейчас мы его не трогаем.
w — то, куда пишется ответ. w.Write отправляет тело. Код состояния мы не указывали, и Go подставил 200 сам — потому что это самый частый случай.
[]byte("Привет!") пока читайте как «эта строка в виде набора байтов». Почему так, станет ясно в уроке про строки и руны, где мы разберём, из чего они сделаны.
Образ. Два лотка на столе. В левый почтальон кладёт пришедшее письмо — это
r, его вы читаете. Правый пустой, и в него вы кладёте ответ — этоw. Перепутать лотки нельзя: из левого не отправляют.
http.ListenAndServe: где слушать
":8080" — порт, тот самый номер квартиры из прошлого урока. Второй аргумент nil означает «бери обработчики, которые я зарегистрировал через HandleFunc». В уроке про маршруты мы поставим туда своё и увидим разницу.
Эта строка и есть причина, по которой терминал замер: программа не закончилась, она слушает.
log.Fatal вокруг неё — на случай, когда слушать не удалось: порт занят, прав не хватило. Тогда программа не умрёт молча, а напишет причину и выйдет. Без этой обёртки самый частый сбой новичка выглядит как «просто ничего не произошло».
Образ. Сесть за окно приёма и открыть его. Пока сидите — принимаете посетителей. Встали и ушли — окно закрыто, и стучать бесполезно.
Почему 8080, а не 80
Порт 80 — стандартный для HTTP, но занимать его на своей машине обычно нельзя без прав администратора. Если IP — это дом, а порт — квартира, то 80 и 443 в этом доме служебные: туда без ключа от управляющей компании не заселишься. 8080 свободен и ничем не хуже для разработки. На настоящем сервере, в уроке про домен и HTTPS, 80 и 443 займёт не ваша программа, а Caddy перед ней.
Что произошло, когда вы открыли страницу
Браузер отправил GET / на localhost порт 8080. Ваша программа приняла запрос, нашла обработчик для /, выполнила его и вернула 200 и тело. Ровно тот обмен, который вы разбирали в уроке о том, как работает интернет — только сервером на этот раз были вы.
Карта урока
Скажите своими словами
Не подглядывая, ответьте вслух или на бумаге. Ответы — в конце урока.
- Почему после
go runтерминал «завис», и почему это правильно? - Что делает
HandleFunc, а чтоListenAndServe? Почему нужны обе? - В коде зарегистрирован только путь
"/". Что программа ответит наlocalhost:8080/about?
Задание
Обязательное. Поменяйте текст ответа на своё имя. Остановите программу, запустите заново, обновите страницу. Всё.
По желанию.
- Добавьте второй адрес: ещё один
http.HandleFuncс путём"/about"и другим текстом. - Посмотрите на свой ответ глазами прошлого урока:
curl -i localhost:8080. Найдите код состояния иcontent-type. Откуда взялсяcontent-type, если вы его не писали? - Поменяйте порт на 3000 и убедитесь, что на 8080 больше никто не отвечает.
Куда это встанет в блоге
Эти строки — скелет вашего блога. Он ещё изменится: у сервера появится свой маршрутизатор, таймауты и корректное завершение. Но форма останется той же, и всё остальное вешается на неё: в уроке про маршруты один путь "/" превратится в десяток адресов, в уроке про шаблоны ответ станет настоящей HTML-страницей, а в уроке про CRUD текст придёт из базы, а не из кода.
Соберите свой блог. Отдельные программы учат языку, а блог растёт из них. Состояние блога после этого урока лежит в репозитории курса — step-1. Сначала напишите сами, потом сверьтесь: сверка после работы учит, сверка вместо работы — нет.
Ответы
Показать ответы
- Потому что
ListenAndServeне возвращает управление: программа слушает порт и ждёт запросов. Завершись она сразу — отвечать было бы некому. Это не зависание, а работа. HandleFuncтолько запоминает, что отвечать на таком-то пути; сама по себе она ничего не слушает.ListenAndServeоткрывает порт и начинает принимать запросы, но без зарегистрированных обработчиков отвечать ей было бы нечем. Одна без другой бесполезна.- Тоже «Привет!». Путь
"/"в Go — не «только главная страница», а «всё, для чего не нашлось более точного правила». Отсюда и ответ на/about, и, кстати, отсутствие 404 там, где вы его ждали.
Источники
Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом
Комментарии (0)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.