Интерфейс 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 напрямую с сервером:
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))Мощь этого интерфейса становится очевидной, когда вы понимаете, что обработчики могут оборачивать другие обработчики. Это позволяет использовать шаблоны промежуточного ПО, добавляя журналирование, аутентификацию или другие сквозные задачи без изменения исходной логики обработчика.
Задание
ЛегкоДавайте создадим простую систему HTTP-обработчиков, которая имитирует обработку веб-запросов! Поскольку в этой среде мы не можем запустить настоящий HTTP-сервер, вы создадите обработчики, реализующие интерфейс http.Handler, и продемонстрируете, как они обрабатывают запросы, работая с имитациями объектов запроса и ответа.
Вы организуете код в двух файлах:
handlers.go: Определите собственные HTTP-обработчики.Создайте структуру
MockResponseWriter, которая captures то, что было бы записано в HTTP-ответ. Она должна хранить тело ответа в виде строки и реализовывать методWrite(data []byte) (int, error), который appends data к телу. Добавьте методBody() stringдля получения accumulated ответа.Создайте структуру
MockRequestс полями-строкамиPathиMethodдля имитации HTTP-запроса.Теперь создайте две структуры обработчиков, которые реализуют паттерн интерфейса
http.Handler(с методомServeHTTP, принимающим*MockResponseWriterи*MockRequest):WelcomeHandlerс полемMessage— записывает сообщение, за которым следует путь запросаMethodHandler— записывает разные ответы based on методе запроса:"Fetching data"для GET,"Creating resource"для POST и"Method not supported"для anything else
main.go: Обрабатывайте запросы с помощью ваших обработчиков.Прочитайте тип обработчика (
welcomeилиmethod), затем прочитайте сведения о запросе. Для обработчика приветствия также прочитайте приветственное сообщение.Создайте appropriate обработчик, сформируйте имитацию запроса и вызовите
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, но паттерн реализации обработчиков остаётся абсолютно тем же.
Попробуйте сами
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()
// Создать mock-запрос
request := &MockRequest{
Path: path,
Method: method,
}
// Создать mock-писатель ответа
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Потренируйтесь самостоятельно: Онлайн-компилятор Go