GinMiddleCoding
Как создать и зарегистрировать собственный middleware в Gin?
Middleware в Gin — это func(*gin.Context) с вызовом c.Next() для передачи управления; регистрируется через r.Use() глобально, через Group().Use() для группы или прямо в маршруте.
Создание и регистрация middleware в Gin
Middleware в Gin — это функция типа gin.HandlerFunc, то есть func(*gin.Context). Ключевой момент: вызов c.Next() передаёт управление следующему handler-у в цепочке, а код после него выполняется на обратном ходу (как defer).
package middleware
import (
"log/slog"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// RequestLogger логирует метод, путь и время выполнения.
func RequestLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// Код ДО handler-а
c.Next() // вызов следующего handler-а
// Код ПОСЛЕ handler-а (обратный ход)
duration := time.Since(start)
slog.Info("request",
"method", c.Request.Method,
"path", c.Request.URL.Path,
"status", c.Writer.Status(),
"duration", duration,
)
}
}
// RequireAuth проверяет Bearer-токен.
func RequireAuth(secret string) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token != "Bearer "+secret {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
})
return // Abort уже остановил цепочку, но return обязателен
}
c.Next()
}
}
Регистрация middleware бывает трёх уровней:
package main
import (
"github.com/gin-gonic/gin"
"myapp/middleware"
)
func main() {
r := gin.New() // без встроенного logger/recovery
// 1. Глобальный уровень — применяется ко всем маршрутам
r.Use(middleware.RequestLogger())
r.Use(gin.Recovery()) // встроенный recovery от паник
// 2. Уровень группы — только для маршрутов внутри /api
api := r.Group("/api", middleware.RequireAuth("secret-token"))
{
api.GET("/profile", profileHandler)
api.GET("/settings", settingsHandler)
}
// 3. Уровень маршрута — только для конкретного endpoint
r.DELETE("/admin/users/:id", middleware.RequireAuth("admin-secret"), deleteUserHandler)
r.Run(":8080")
}
func profileHandler(c *gin.Context) { c.JSON(200, gin.H{"user": "alice"}) }
func settingsHandler(c *gin.Context) { c.JSON(200, gin.H{"theme": "dark"}) }
func deleteUserHandler(c *gin.Context) {
c.JSON(200, gin.H{"deleted": c.Param("id")})
}
Middleware может хранить данные в контексте для передачи вниз по цепочке:
func InjectUserID() gin.HandlerFunc {
return func(c *gin.Context) {
userID := extractUserIDFromToken(c.GetHeader("Authorization"))
c.Set("userID", userID) // записываем в контекст
c.Next()
}
}
// В handler-е:
func profileHandler(c *gin.Context) {
userID, _ := c.Get("userID")
c.JSON(200, gin.H{"user_id": userID})
}
func extractUserIDFromToken(auth string) string {
// упрощённая заглушка
if len(auth) > 7 {
return auth[7:]
}
return ""
}
Подводные камни
- Если не вызвать
c.Next()и не вызватьc.Abort(), последующие handler-ы в цепочке просто не выполнятся — молчаливый баг. c.Abort()выставляет флаг, но не останавливает текущую функцию middleware — нужен явныйreturnпосле вызова.- Порядок
r.Use()имеет значение: middleware, зарегистрированный послеr.Group(), не применяется к маршрутам этой группы, если группа создана до вызоваUse. c.Set/c.Getне потокобезопасны вне goroutine текущего запроса — не передавайте*gin.Contextв фоновые горутины; копируйте нужные данные до запуска горутины.- Паника внутри middleware без
gin.Recovery()уронит весь сервер; не используйтеgin.New()без явного добавления Recovery. - Большое количество middleware, измеряющих время через
time.Now(), складывается в заметный overhead на hot-path — профилируйте перед добавлением каждого нового слоя. - Middleware, написанный как замыкание с состоянием (например, счётчик), должен защищать shared state мьютексом или использовать
sync/atomic.
Common mistakes
- Давать ответ про собственный middleware в Gin только на уровне определения, не показывая поведение в реальном приложении.
- Игнорировать границы ответственности вокруг темы «собственный middleware в Gin»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
- Не связывать собственный middleware в Gin с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.
What the interviewer is testing
- Точно объясняет, что именно делает собственный middleware в Gin и где это используется в Go-коде.
- Связывает собственный middleware в Gin с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
- Не изобретает API и опирается на реальные контракты официальной документации.