Обработка запросов и современный роутинг в Go 1.22+

Мы переходим к этапу, когда приложение ожидает запросов извне. В прошлой теме вы научились упаковывать данные в JSON. Теперь мы создадим точку входа (endpoint), которая примет HTTP-запрос от вашего фронтенда на React и вернет эти данные.

В Go 1.22 стандартный роутер обновился: теперь он умеет то, ради чего раньше ставили сторонние библиотеки. Мы построим API, используя только возможности стандартного пакета net/http.

Анатомия HTTP-сервера

Для работы с сетью в Go используется пакет net/http. Если в Node.js для сервера обычно нужен Express, то в Go всё необходимое встроено в стандартную библиотеку.

Главный диспетчер запросов — ServeMux (мультиплексор). Он сопоставляет URL входящего запроса с нужной функцией-обработчиком. Процесс обработки запроса показан в Схеме 1.

Маршрутизация в Go 1.22+

Раньше стандартный роутер не умел различать методы (GET/POST) и не поддерживал динамические параметры вроде /users/{id}. Разработчикам приходилось писать проверки вручную или использовать внешние роутеры (например, chi или gin).

В современных версиях Go синтаксис стал лаконичным. Метод и переменные указываются прямо в строке маршрута:

mux := http.NewServeMux()

// Маршрут с методом и динамическим параметром {id}
mux.HandleFunc("GET /tasks/{id}", getTaskHandler)

// Маршрут для создания задачи
mux.HandleFunc("POST /tasks", createTaskHandler)

Обработчик запросов: HandlerFunc

Любой эндпоинт обслуживается функцией с сигнатурой HandlerFunc. У нее всегда два аргумента:

  1. ResponseWriter — интерфейс для отправки ответа. Через него вы записываете заголовки, статус-коды и тело ответа.
  2. Request — структура с данными входящего запроса (URL, заголовки, body).

Рассмотрим, как извлечь ID из URL и вернуть JSON:

func getTaskHandler(w http.ResponseWriter, r *http.Request) {
    // Извлекаем параметр {id} из пути
    id := r.PathValue("id")
    
    // Подготовим данные (как в прошлом уроке)
    task := Task{ID: id, Title: "Изучить роутинг в Go", Done: false}

    // Устанавливаем заголовок
    w.Header().Set("Content-Type", "application/json")
    
    // Кодируем структуру в JSON и отправляем в ResponseWriter
    json.NewEncoder(w).Encode(task)
}

Типичные ошибки

Распространенная ошибка при переходе с TypeScript — продолжать выполнение функции после отправки ошибки.

func badHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        // ОШИБКА: Код пойдет дальше и попытается записать "Успех" в закрытый ответ.
    }
    w.Write([]byte("Успех"))
}

В Go используется паттерн «проверь и вернись»: если возникла ошибка, отправьте её через http.Error и немедленно прервите выполнение функции с помощью return.

Запуск сервера

Чтобы сервер начал принимать соединения, нужно вызвать функцию http.ListenAndServe.

Создайте файл main.go и реализуйте сервер, который:

  1. Слушает порт :8080.
  2. Имеет маршрут GET /status, возвращающий текст "OK" и статус-код 200.
  3. Использует ServeMux для регистрации маршрутов.

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

В следующей теме мы разберем, как использовать Middleware, чтобы не дублировать общий код в каждом хендлере.

Понравился урок?

Сохраните прогресс и получите персональный курс по любой теме — без форм и паролей

Продолжить в Telegram