Перед дальнейшей работой, давайте проясним один момент касательно интерфейса http.Handler. Он может показаться немного сложным, но не волнуйтесь. Продолжайте работать над веб-приложением и возвращайтесь к данной теме, когда у вас возникнут какие либо трудности с пониманием работы обработчиков HTTP запросов.
Содержание статьи
- Функция в качестве обработчика запросов
- Объединение обработчиков в цепочку
- Многопоточная обработка запросов
В предыдущих уроках мы часто использовали термин обработчик, особо не объясняя, что именно он значит. Грубо говоря, под обработчиком подразумевается объект, который удовлетворяет интерфейсу http.Handler:
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
1 2 3 |
type Handler interface { ServeHTTP(ResponseWriter, *Request) } |
Если объяснить по-простому, это значит, что для того чтобы объект считался обработчиком, у него должен быть метод ServeHTTP()
со следующей сигнатурой:
1 |
ServeHTTP(http.ResponseWriter, *http.Request) |
Самая простая форма обработчика будет выглядеть наподобие следующего примера:
1 2 3 4 5 |
type home struct {} func (h *home) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Это моя домашняя страница")) } |
Здесь у нас есть объект (в данном случае это структура home
, но это также может быть строка, функция или что-то другое), и мы реализовали для неё метод с сигнатурой ServeHTTP(http.ResponseWriter, *http.Request)
. Это все, что требуется для создания обработчика HTTP запросов.
Затем можно зарегистрировать обработчик с помощью servemux (маршрутизатор HTTP запросов) , используя метод Handle
следующим образом:
1 2 |
mux := http.NewServeMux() mux.Handle("/", &home{}) |
Функция в качестве обработчика запросов
Сейчас создание объекта только для того, чтобы можно было реализовать для него метод ServeHTTP()
, кажется многословным и запутанным. Именно поэтому на практике гораздо чаще обработчики пишутся как обычные функции (как мы это делали до сих пор).
Например:
1 2 3 |
func home(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Моя домашняя страница!")) } |
Функция home()
является самой обычной функцией. У нее нет метода ServeHTTP()
. Так, что по сути она не является обработчиком для HTTP запросов.
Нам требуется превратить функцию в обработчик с помощью использования адаптера http.HandlerFunc() следующим образом:
1 2 |
mux := http.NewServeMux() mux.Handle("/", http.HandlerFunc(home)) |
Адаптер http.HandlerFunc()
работает путем автоматического добавления метода ServeHTTP()
для функции home()
. При выполнении, метод ServeHTTP()
просто вызывает контент из оригинальной функции home()
. Это обходной, но удобный способ заставить обычную функцию соответствовать требованиям интерфейса http.Handler
.
В данном учебнике по созданию сайта на Go, для регистрации функций в качестве обработчиков с помощью servemux, мы использовали метод HandleFunc()
. Это синтаксический сахар, который сразу превращает функцию в обработчик и регистрирует её, вместо того, чтобы это делать нам вручную.
Такой формат более компактный и выполняет ту же задачу как и код выше:
1 2 |
mux := http.NewServeMux() mux.HandleFunc("/", home) |
Объединение обработчиков в цепочку
Вы могли заметить кое-что интересное прямо на старте проекта. Функция http.ListenAndServe() принимает объект http.Handler
в качестве второго параметра…
1 |
func ListenAndServe(addr string, handler Handler) error |
… но мы передавали servemux вместо http.Handler.
Мы смогли это сделать, потому что у servemux также есть метод ServeHTTP()
. Это значит, что он также соответствует требованиям интерфейса http.Handler
.
Проще думать о servemux как об особом обработчике, который вместо предоставления ответа передает запрос второму обработчику. Это не такой уж большой скачок, как может показаться на первый взгляд. Объединение обработчиков в цепочку — очень распространенная идиома в Go, которую мы часто будем использовать в будущем при работе с нашим веб-проектом.
На самом деле происходит следующее:
- Когда сервер получает новый HTTP-запрос, он вызывает метод
ServeHTTP()
от servemux; - Он ищет соответствующий обработчик на основе URL запроса и, в свою очередь, вызывает метод
ServeHTTP()
данного обработчика.
Можно рассматривать веб-приложение как цепочку из ServeHTTP()
методов, вызываемых один за другим.
Многопоточная обработка запросов
Важно отметить еще один момент: все входящие HTTP-запросы выполняются в собственной горутине. Для загруженных серверов это означает, что код, загружаемый обработчиками или вызываемый ими, скорее всего будет выполняться параллельно. Это ускоряет работу приложения, но вам нужно знать и защититься от состоянии гонки при доступе к общим ресурсам из обработчиков.
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»