chi для Go: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое chi и зачем она нужна?

chi — это легковесный, высокопроизводительный и идиоматичный HTTP-роутер для языка программирования Go. Он создан для разработки веб-приложений и API, предоставляя разработчикам мощный и гибкий инструмент для маршрутизации HTTP-запросов.

В отличие от стандартного пакета net/http, chi предлагает:

  • Совместимость с net/http: chi полностью реализует интерфейс http.Handler, что позволяет легко интегрироваться с существующим Go-кодом и middleware.
  • Контекстный роутинг: chi использует context.Context для передачи параметров маршрута (например, ID пользователя) между middleware и обработчиками.
  • Вложенные маршруты (Subrouters): Позволяет группировать маршруты по логическим блокам (например, /api/v1/users).
  • Группировка middleware: Можно применять middleware как ко всему роутеру, так и к отдельным группам маршрутов.
  • Встроенная поддержка CORS, логирования, сжатия и других распространённых задач.

chi идеально подходит для создания REST API, микросервисов и любых веб-приложений, где требуется чистая, производительная и легко тестируемая архитектура.

Установка chi

Для установки chi используйте стандартный менеджер пакетов Go. Убедитесь, что у вас установлен Go (версии 1.14 или выше).

go get -u github.com/go-chi/chi/v5

После выполнения этой команды библиотека будет добавлена в ваш go.mod файл. Если вы используете Go Modules (рекомендуется), импорт будет выглядеть так:

import "github.com/go-chi/chi/v5"

Также часто устанавливают пакет middleware:

go get -u github.com/go-chi/cors
go get -u github.com/go-chi/chi/v5/middleware

Быстрый старт с примером кода

Давайте создадим простой HTTP-сервер, который отвечает на GET-запросы.

package main

import (
    "fmt"
    "net/http"

    "github.com/go-chi/chi/v5"
)

func main() {
    r := chi.NewRouter()

    // Определяем маршрут
    r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, chi!")
    })

    // Запускаем сервер
    http.ListenAndServe(":3000", r)
}

Запустите этот код и откройте браузер по адресу http://localhost:3000/hello. Вы увидите сообщение "Hello, chi!".

Основные методы и функции chi

Рассмотрим ключевые методы библиотеки chi. Все примеры предполагают, что у вас уже создан роутер r := chi.NewRouter().

1. Get(pattern string, handlerFn http.HandlerFunc)

Что делает: Регистрирует обработчик для HTTP-метода GET по указанному шаблону пути.

Параметры:

  • pattern — строка пути (например, /users, /users/{id}).
  • handlerFn — функция-обработчик, соответствующая сигнатуре http.HandlerFunc.

Пример:

r.Get("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Главная страница"))
})

2. Post(pattern string, handlerFn http.HandlerFunc)

Что делает: Регистрирует обработчик для HTTP-метода POST.

Параметры: Аналогично Get.

Пример:

r.Post("/users", func(w http.ResponseWriter, r *http.Request) {
    // Логика создания пользователя
    w.WriteHeader(http.StatusCreated)
    w.Write([]byte("Пользователь создан"))
})

3. Put(pattern string, handlerFn http.HandlerFunc)

Что делает: Регистрирует обработчик для HTTP-метода PUT (обычно используется для полного обновления ресурса).

Параметры: Аналогично Get.

Пример:

r.Put("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
    userID := chi.URLParam(r, "id")
    w.Write([]byte("Обновлён пользователь с ID: " + userID))
})

4. Delete(pattern string, handlerFn http.HandlerFunc)

Что делает: Регистрирует обработчик для HTTP-метода DELETE.

Параметры: Аналогично Get.

Пример:

r.Delete("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
    userID := chi.URLParam(r, "id")
    w.Write([]byte("Удалён пользователь с ID: " + userID))
})

5. Handle(pattern string, handler http.Handler)

Что делает: Регистрирует любой объект, реализующий интерфейс http.Handler, для всех HTTP-методов по указанному пути. Это полезно для подключения сторонних обработчиков или статических файлов.

Параметры:

  • pattern — строка пути.
  • handler — объект, реализующий http.Handler.

Пример (раздача статических файлов):

r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

6. Route(pattern string, fn func(r chi.Router))

Что делает: Создаёт вложенный роутер (subrouter) для группировки маршрутов. Все маршруты, определённые внутри функции fn, будут иметь префикс pattern.

Параметры:

  • pattern — префикс пути (например, /api/v1).
  • fn — функция, которая получает новый экземпляр chi.Router для определения вложенных маршрутов.

Пример:

r.Route("/api", func(r chi.Router) {
    r.Get("/users", getUsers)
    r.Post("/users", createUser)
    r.Route("/users/{userID}", func(r chi.Router) {
        r.Get("/", getUserByID)
        r.Put("/", updateUser)
    })
})

7. Group(fn func(r chi.Router))

Что делает: Позволяет сгруппировать несколько маршрутов без добавления префикса к пути. Часто используется для применения middleware к группе маршрутов.

Параметры:

  • fn — функция, которая получает новый экземпляр chi.Router.

Пример (применение middleware к группе):

r.Group(func(r chi.Router) {
    r.Use(middleware.Logger) // Логирование только для этой группы
    r.Get("/admin", adminHandler)
    r.Get("/admin/users", adminUsersHandler)
})
Use(middlewares ...func(http.Handler) http.Handler)

Что делает: Добавляет один или несколько middleware к роутеру. Middleware выполняются до обработчика маршрута и могут модифицировать запрос/ответ или прерывать цепочку обработки.

Параметры:

  • middlewares — одна или несколько функций middleware.

Пример:

r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)

9. With(middlewares ...func(http.Handler) http.Handler) chi.Router

Что делает: Создаёт новый вложенный роутер, который наследует middleware от родительского, но также добавляет указанные. Полезно для изоляции middleware для конкретного подмножества маршрутов.

Параметры:

  • middlewares — middleware для добавления.

Пример:

r.With(middleware.BasicAuth("realm", creds)).Get("/protected", protectedHandler)

10. chi.URLParam(r *http.Request, key string) string

Что делает: Извлекает значение параметра пути (path parameter) из URL. Параметры определяются в шаблоне маршрута с помощью фигурных скобок (например, /users/{id}).

Параметры:

  • r — объект *http.Request.
  • key — имя параметра (строка).

Пример:

r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    w.Write([]byte("User ID: " + id))
})

11. chi.URLParamFromCtx(ctx context.Context, key string) string

Что делает: Альтернативный способ получения параметра URL из контекста запроса. Полезно, если у вас нет прямого доступа к *http.Request.

Параметры:

  • ctx — контекст (context.Context).
  • key — имя параметра.

Пример:

func myMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
        userID := chi.URLParamFromCtx(ctx, "userID")
        // ...
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

12. chi.RouteContext(r *http.Request) *chi.Context

Что делает: Возвращает контекст маршрута chi, который содержит информацию о текущем маршруте, включая все параметры URL. Это более низкоуровневый доступ, чем URLParam.

Параметры:

  • r — объект *http.Request.

Пример:

r.Get("/articles/{slug}", func(w http.ResponseWriter, r *http.Request) {
    routeCtx := chi.RouteContext(r)
    slug := routeCtx.URLParam("slug")
    // ...
})

Реальный практический пример использования

Создадим REST API для управления списком задач (Todo List).

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"
    "sync"

    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
    "github.com/go-chi/cors"
)

type Todo struct {
    ID    int    `json:"id"`
    Title string `json:"title"`
    Done  bool   `json:"done"`
}

var (
    todos   = make(map[int]Todo)
    nextID  = 1
    mu      sync.Mutex
)

func main() {
    r := chi.NewRouter()

    // Middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(cors.Handler(cors.Options{
        AllowedOrigins:   []string{"https://*", "http://*"},
        AllowedMethods:   []                    

Рекомендаций