Интерфейс http.Handler
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 77 из 107.
Интерфейс http.Handler является основой архитектуры веб-сервера Go. Он определяет, как обрабатываются HTTP-запросы, и любой тип, реализующий его, может обслуживать веб-запросы.
Интерфейс удивительно прост:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Любой тип с методом ServeHTTP может обрабатывать HTTP-запросы. Вот пример пользовательского обработчика:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Вы можете использовать этот обработчик напрямую с сервером:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)Для более простых случаев Go предоставляет http.HandlerFunc — тип, который позволяет преобразовать обычную функцию в обработчик:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))Мощь этого интерфейса становится очевидной, когда вы понимаете, что обработчики могут оборачивать другие обработчики. Это позволяет использовать паттерны промежуточного ПО (middleware), в которых вы добавляете логирование, аутентификацию или другие сквозные задачи без изменения логики исходного обработчика.
Задание
ЛегкоДавайте создадим простую систему HTTP-обработчиков, которая имитирует процесс обработки веб-запросов! Поскольку в этой среде мы не можем запустить настоящий HTTP-сервер, вы создадите обработчики, реализующие интерфейс http.Handler, и продемонстрируете, как они обрабатывают запросы, работая с макетами (mock) объектов запроса и ответа.
Вы организуете свой код в двух файлах:
handlers.go: Определите ваши пользовательские HTTP-обработчики.Создайте структуру
MockResponseWriter, которая фиксирует то, что было бы записано в HTTP-ответ. Она должна хранить тело ответа в виде строки и реализовывать методWrite(data []byte) (int, error), который добавляет данные в тело. Добавьте методBody() stringдля получения накопленного ответа.Создайте структуру
MockRequestсо строковыми полямиPathиMethodдля имитации HTTP-запроса.Теперь создайте две структуры обработчиков, которые реализуют паттерн интерфейса
http.Handler(с методомServeHTTP, который принимает*MockResponseWriterи*MockRequest):WelcomeHandlerс полемMessage— записывает сообщение, за которым следует путь запросаMethodHandler— записывает разные ответы в зависимости от метода запроса:"Fetching data"для GET,"Creating resource"для POST и"Method not supported"для всего остального
main.go: Обрабатывайте запросы через ваши обработчики.Считайте тип обработчика (
welcomeилиmethod), затем считайте детали запроса. Для обработчика welcome также считайте приветственное сообщение.Создайте соответствующий обработчик, создайте макет запроса и вызовите
ServeHTTPдля его обработки. Выведите тело ответа.
Будут предоставлены следующие входные данные:
- Строка 1: Тип обработчика (
welcomeилиmethod) - Строка 2: Путь запроса
- Строка 3: Метод запроса
- Строка 4 (только для welcome): Приветственное сообщение
Например, при вводе:
welcome
/home
GET
Hello fromВаш вывод должен быть:
Hello from /homeИ при вводе:
method
/api/users
POSTВаш вывод должен быть:
Creating resourceИ при вводе:
method
/api/data
GETВаш вывод должен быть:
Fetching dataИ при вводе:
method
/api/items
DELETEВаш вывод должен быть:
Method not supportedЭта задача демонстрирует основную концепцию интерфейса http.Handler — любой тип с методом ServeHTTP может обрабатывать запросы. В реальных приложениях http.ResponseWriter и *http.Request берутся из стандартной библиотеки Go, но паттерн реализации обработчиков остается точно таким же.
Шпаргалка
Интерфейс http.Handler является основой архитектуры веб-сервера Go:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Любой тип с методом ServeHTTP может обрабатывать HTTP-запросы:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Регистрация обработчика на сервере:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)Для более простых случаев используйте http.HandlerFunc, чтобы преобразовать функцию в обработчик:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))Обработчики могут оборачивать другие обработчики, что позволяет реализовывать паттерны промежуточного ПО (middleware) для логирования, аутентификации или других сквозных задач.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Чтение типа обработчика
scanner.Scan()
handlerType := scanner.Text()
// Чтение пути запроса
scanner.Scan()
path := scanner.Text()
// Чтение метода запроса
scanner.Scan()
method := scanner.Text()
// Создание мок-запроса
request := &MockRequest{
Path: path,
Method: method,
}
// Создание мок-объекта для записи ответа
response := &MockResponseWriter{}
// TODO: На основе handlerType создать соответствующий обработчик
// Если handlerType равен "welcome", прочитать приветственное сообщение и создать WelcomeHandler
// Если handlerType равен "method", создать MethodHandler
// Вызвать ServeHTTP у обработчика с response и request
if handlerType == "welcome" {
scanner.Scan()
message := scanner.Text()
// TODO: Создать WelcomeHandler с сообщением и вызвать ServeHTTP
_ = message // Удалите эту строку при реализации
} else if handlerType == "method" {
// TODO: Создать MethodHandler и вызвать ServeHTTP
}
// Вывод тела ответа
fmt.Println(response.Body())
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool