Menu
Coddy logo textTech

Интерфейс 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), в которых вы добавляете логирование, аутентификацию или другие сквозные задачи без изменения логики исходного обработчика.

challenge icon

Задание

Легко

Давайте создадим простую систему 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())
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование