Что такое 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)
})
Что делает: Добавляет один или несколько 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: []