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 напрямую с сервером:

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))

Мощь этого интерфейса становится очевидной, когда вы понимаете, что обработчики могут оборачивать другие обработчики. Это позволяет использовать шаблоны промежуточного ПО, добавляя журналирование, аутентификацию или другие сквозные задачи без изменения исходной логики обработчика.

challenge icon

Задание

Легко

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

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор Go