Перед дальнейшей работой, давайте проясним один момент касательно интерфейса http.Handler. Он может показаться немного сложным, но не волнуйтесь. Продолжайте работать над веб-приложением и возвращайтесь к данной теме, когда у вас возникнут какие либо трудности с пониманием работы обработчиков HTTP запросов.

Содержание статьи

В предыдущих уроках мы часто использовали термин обработчик, особо не объясняя, что именно он значит. Грубо говоря, под обработчиком подразумевается объект, который удовлетворяет интерфейсу http.Handler:

Премиум 👑 канал по Golang

Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎

Подписаться на канал

Уроки, статьи и Видео

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Go в ВК ЧАТ в Telegram

Если объяснить по-простому, это значит, что для того чтобы объект считался обработчиком, у него должен быть метод ServeHTTP() со следующей сигнатурой:

Самая простая форма обработчика будет выглядеть наподобие следующего примера:

Здесь у нас есть объект (в данном случае это структура home, но это также может быть строка, функция или что-то другое), и мы реализовали для неё метод с сигнатурой ServeHTTP(http.ResponseWriter, *http.Request). Это все, что требуется для создания обработчика HTTP запросов.

Затем можно зарегистрировать обработчик с помощью servemux (маршрутизатор HTTP запросов) , используя метод Handle следующим образом:

Функция в качестве обработчика запросов

Сейчас создание объекта только для того, чтобы можно было реализовать для него метод ServeHTTP(), кажется многословным и запутанным. Именно поэтому на практике гораздо чаще обработчики пишутся как обычные функции (как мы это делали до сих пор).

Например:

Функция home() является самой обычной функцией. У нее нет метода ServeHTTP(). Так, что по сути она не является обработчиком для HTTP запросов.

Нам требуется превратить функцию в обработчик с помощью использования адаптера http.HandlerFunc() следующим образом:

Адаптер http.HandlerFunc() работает путем автоматического добавления метода ServeHTTP() для функции home(). При выполнении, метод ServeHTTP() просто вызывает контент из оригинальной функции home(). Это обходной, но удобный способ заставить обычную функцию соответствовать требованиям интерфейса http.Handler.

В данном учебнике по созданию сайта на Go, для регистрации функций в качестве обработчиков с помощью servemux, мы использовали метод HandleFunc(). Это синтаксический сахар, который сразу превращает функцию в обработчик и регистрирует её, вместо того, чтобы это делать нам вручную.

Такой формат более компактный и выполняет ту же задачу как и код выше:

Объединение обработчиков в цепочку

Вы могли заметить кое-что интересное прямо на старте проекта. Функция http.ListenAndServe() принимает объект http.Handler в качестве второго параметра…

… но мы передавали servemux вместо http.Handler.

Мы смогли это сделать, потому что у servemux также есть метод ServeHTTP(). Это значит, что он также соответствует требованиям интерфейса http.Handler.

Проще думать о servemux как об особом обработчике, который вместо предоставления ответа передает запрос второму обработчику. Это не такой уж большой скачок, как может показаться на первый взгляд. Объединение обработчиков в цепочку — очень распространенная идиома в Go, которую мы часто будем использовать в будущем при работе с нашим веб-проектом.

На самом деле происходит следующее:

  1. Когда сервер получает новый HTTP-запрос, он вызывает метод ServeHTTP() от servemux;
  2. Он ищет соответствующий обработчик на основе URL запроса и, в свою очередь, вызывает метод ServeHTTP() данного обработчика.

Можно рассматривать веб-приложение как цепочку из ServeHTTP() методов, вызываемых один за другим.

Многопоточная обработка запросов

Важно отметить еще один момент: все входящие HTTP-запросы выполняются в собственной горутине. Для загруженных серверов это означает, что код, загружаемый обработчиками или вызываемый ими, скорее всего будет выполняться параллельно. Это ускоряет работу приложения, но вам нужно знать и защититься от состоянии гонки при доступе к общим ресурсам из обработчиков.