- Реализуем веб-приложение на Go
- Как сервер сопоставляет пути и обработчики
- Как работать с параметрами запросов
- Выводы
Все веб-сайты работают по одному и тому же принципу. Всегда есть клиент и сервер, которые взаимодействуют друг с другом:
- Клиент отправляет запрос
- Сервер обрабатывает запрос
- Сервер отправляет ответ клиенту
Такая последовательность называется циклом «запрос-обработка-ответ».
Веб-приложения постоянно обрабатывают запросы и формируют ответы на них. Чтобы упростить эту задачу, можно воспользоваться библиотеками, которые абстрагируют в себе основную работу с запросом и ответом.
Библиотеки упрощают работу над циклом «запрос-обработка-ответ» и освобождают время разработчика для более важных задач. Например, он может сосредоточиться на архитектуре или бизнес-логике своего приложения.
Как во многих других языках, в Golang есть готовые библиотеки для обработки веб-запросов. В этом уроке мы изучим, как реализовать веб-приложение с помощью библиотек.
Реализуем веб-приложение на Go
В этом уроке мы будем использовать пакет стандартной библиотеки net/http
. С его помощью можно написать веб-приложение буквально в несколько строк кода:
package main
import (
"net/http"
)
func main() {
// обозначаем, что на запрос по пути "/" возвращается строка "Hello World"
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// тело ответа — это массив байт
w.Write([]byte("Hello world!"))
})
// запускаем веб-приложение для обработки запросов
http.ListenAndServe(":80", nil)
}
Пробуем запустить веб-приложение:
go run main.go
Открываем любой браузер и переходим по адресу http://localhost — мы должны получить в ответ строку Hello world!
. А теперь разберем этот пример по циклу «запрос-обработка-ответ»:
- Веб-браузер выступает в роли клиента, а Go-приложение — в роли сервера
- Клиент отправляет HTTP-запрос по пути
/
- Сервер читает запрос, обрабатывает его и возвращает строку
Hello world!
Вернемся к примеру и рассмотрим подробнее, что происходит в каждой строчке приложения.
Сначала мы вызывали функцию http.HandleFunc
:
// Обозначаем, что на запрос по пути "/" возвращается строка "Hello World!"
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Тело ответа — это массив byte
w.Write([]byte("Hello world!"))
})
Эта функция настраивает обработчик по заданному пути. В нашем примере при любом запросе по пути /
всегда будет возвращаться строка Hello world!
.
Дальше мы использовали функцию http.ListenAndServe
:
// Запускаем веб-приложение для обработки запросов
http.ListenAndServe(":80", nil)
В этой части кода запускается веб-приложение на 80 порту и слушает входящие запросы. Все входящие запросы будут обработаны согласно логике, описанной в обработчиках http.HandleFunc
.
Вся бизнес-логика приложения находится внутри обработчиков. Попробуем разобраться в их работе и изучим, как они обрабатывают запросы с различными путями и параметрами.
Как сервер сопоставляет пути и обработчики
Когда сервер получает запрос, он смотрит, по какому пути пришел запрос — таким образом он определяет обработчика. Этот процесс сопоставления обработчика по пути называется маршрутизацией или роутингом.
Чтобы использовать роутинг различных запросов на уровне кода Go-приложения, нужно описать обработчиков http.HandleFunc
под каждый путь запроса. Рассмотрим, как будет выглядеть код веб-приложения с двумя обработчиками на примере платформы Хекслет:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome to Hexlet"))
})
http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hexlet is the leading educational platform for Software Engineers"))
})
http.ListenAndServe(":80", nil)
}
Запускаем веб-приложение, открываем браузер и проверяем:
- Переходим на страницу http://localhost/ — получаем в ответ строку
Welcome to Hexlet
- Переходим на http://localhost/about — получаем строку
Hexlet is the leading educational platform for Software Engineers
Так веб-приложение понимает, по какому пути пришел запрос, и выбирает под него соответствующий обработчик. Мы фокусируемся только на том, что находится внутри обработчиков. При этом реализация роутинга абстрагирована внутри пакета net/http
.
Эта реализация обработчиков хорошо работает, когда мы работаем со статичными страницами. Но как быть, если нужно вывести много информации, которая не вмещается на одну страницу?
Как работать с параметрами запросов
Усложним пример с платформой Хекслета. Предположим, что нам надо вывести список курсов на странице/courses
, но не более 5 курсов за раз. В этом случае нужно просто разбить список курсов постранично. Такое разбиение данных называется пагинацией (от английского pagination — «разбиение по страницам»).
Самый популярный подход к пагинации — использование параметров запроса. Например, на страницу приходит запрос по пути /courses?page=1
— по параметру page видим, что запрашивается первая страница — возвращаем курсы с 1 по 5.
При получении запроса веб-приложение на net/http
считывает всю информацию и заполняет объект r *http.Request
.
Теперь попробуем реализовать пагинацию в коде, считав параметры запроса:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome to Hexlet"))
})
http.HandleFunc("/courses", func(w http.ResponseWriter, r *http.Request) {
// считываем параметр page из запроса
page := r.URL.Query().Get("page")
// рассчитываем, какую страницу нужно вернуть
var pageCourses string
switch page {
case "":
pageCourses="Введите номер курса!"
case "1":
pageCourses="Как написать свой первый \"Hello world\" на go..."
case "2":
pageCourses="Как работает сборщик мусора в go..."
default:
pageCourses="Курс в разработке..."
}
// возвращаем страницу курса
w.Write([]byte(pageCourses))
})
http.ListenAndServe(":80", nil)
}
Выводы
- Язык Go позволяет "из коробки" реализовать веб-приложение, используя стандартный пакет
net/http
- Настройка маршрутизации веб-приложения в Go осуществляется с помощью функции
http.HandleFunc
, которая задает функцию-обработчика на каждый путь HTTP-запроса - Вся информация о HTTP-запросе лежит в объекте
r *http.Request
- Ответ клиенту формируется с помощью функций объекта
w http.ResponseWriter
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.