Цепочки промежуточного ПО
Луковая модель
Промежуточное ПО HTTPC использует луковую модель: запрос проходит снаружи внутрь, ответ — изнутри наружу:
Запрос → Recovery → Logging → RequestID → Handler
↓
Ответ ← Recovery ← Logging ← RequestID ← Responsecfg := httpc.DefaultConfig()
cfg.Middleware.Middlewares = []httpc.MiddlewareFunc{
httpc.RecoveryMiddleware(), // Внешний слой: восстановление после panic
httpc.LoggingMiddleware(log.Printf), // Второй слой: логирование
httpc.RequestIDMiddleware("X-Request-ID", nil), // Внутренний слой: Request ID
}
client, _ := httpc.New(cfg)Встроенное промежуточное ПО
RecoveryMiddleware
Восстановление после panic, предотвращает падение процесса:
httpc.RecoveryMiddleware()LoggingMiddleware
Логирование запросов/ответов, URL автоматически маскируется:
httpc.LoggingMiddleware(func(format string, args ...any) {
log.Printf("[HTTP] "+format, args...)
})
// Пример вывода: [HTTP] GET https://api.example.com/data -> 200 (150ms) (код состояния и длительность — фактические измерения, не фиксированные значения)RequestIDMiddleware
Добавляет уникальный ID каждому запросу, генерируется с использованием crypto/rand:
httpc.RequestIDMiddleware("X-Request-ID", nil) // 32-символьный hex по умолчанию
// Пользовательский генератор
httpc.RequestIDMiddleware("X-Request-ID", func() string {
return uuid.New().String()
})TimeoutMiddleware
Таймаут на уровне промежуточного ПО, принудительно срабатывает до таймаута клиента:
httpc.TimeoutMiddleware(30 * time.Second)Не используйте для Download или потоковых запросов
defer cancel() в TimeoutMiddleware срабатывает сразу после возврата обработчика (т.е. после получения заголовков ответа), поэтому для запросов Download или WithStreamBody контекст отменяется до чтения тела ответа, что проявляется как ошибка «context canceled». Для потоковых сценариев и загрузок используйте опцию WithTimeout.
HeaderMiddleware
Добавляет статические заголовки ко всем запросам:
httpc.HeaderMiddleware(map[string]string{
"X-App-Version": "1.0.0",
"X-Platform": "server",
})MetricsMiddleware
Сбор метрик запросов:
httpc.MetricsMiddleware(func(method, url string, statusCode int, duration time.Duration, err error) {
metrics.IncrCounter("http.requests", 1)
metrics.RecordTimer("http.latency", duration)
if err != nil {
metrics.IncrCounter("http.errors", 1)
}
})AuditMiddleware
Безопасность аудит для финансовых, медицинских и других сценариев с требованиями соответствия:
httpc.AuditMiddleware(func(event httpc.AuditEvent) {
log.Printf("[AUDIT] %s %s -> %d (%v)",
event.Method, event.URL, event.StatusCode, event.Duration)
})AuditMiddlewareWithConfig
Настраиваемое промежуточное ПО аудита:
auditCfg := &httpc.AuditMiddlewareConfig{
Format: "json",
IncludeHeaders: true,
MaskHeaders: []string{"Authorization", "Cookie"},
SanitizeError: true,
}
httpc.AuditMiddlewareWithConfig(func(event httpc.AuditEvent) {
data, err := json.Marshal(event)
if err != nil {
log.Println("Ошибка сериализации события аудита:", err)
return
}
log.Println(string(data))
}, auditCfg)События аудита поддерживают извлечение SourceIP и UserID из контекста:
ctx := context.WithValue(context.Background(), httpc.SourceIPKey, "192.168.1.1")
ctx = context.WithValue(ctx, httpc.UserIDKey, "user-123")Ручная цепочечная композиция
Используйте функцию Chain для объединения промежуточного ПО:
middleware := httpc.Chain(
httpc.RecoveryMiddleware(),
httpc.LoggingMiddleware(log.Printf),
httpc.RequestIDMiddleware("X-Request-ID", nil),
)
cfg := httpc.DefaultConfig()
cfg.Middleware.Middlewares = []httpc.MiddlewareFunc{middleware}Пользовательское промежуточное ПО
func CORSMiddleware(origin string) httpc.MiddlewareFunc {
return func(next httpc.Handler) httpc.Handler {
return func(ctx context.Context, req httpc.RequestMutator) (httpc.ResponseMutator, error) {
// Фаза запроса: модификация запроса
req.SetHeader("Origin", origin)
// Вызов следующего обработчика
resp, err := next(ctx, req)
// Фаза ответа: запись или модификация ответа
if resp != nil {
log.Printf("Статус ответа: %d", resp.StatusCode())
}
return resp, err
}
}
}Промежуточное ПО с размыкателем цепи
func CircuitBreakerMiddleware(threshold int) httpc.MiddlewareFunc {
var failures int
var mu sync.Mutex
return func(next httpc.Handler) httpc.Handler {
return func(ctx context.Context, req httpc.RequestMutator) (httpc.ResponseMutator, error) {
mu.Lock()
if failures >= threshold {
mu.Unlock()
return nil, fmt.Errorf("circuit breaker open")
}
mu.Unlock()
resp, err := next(ctx, req)
if err != nil {
mu.Lock()
failures++
mu.Unlock()
}
return resp, err
}
}
}Конфигурация промежуточного ПО
cfg := httpc.DefaultConfig()
cfg.Middleware = &httpc.MiddlewareConfig{
Middlewares: []httpc.MiddlewareFunc{
httpc.RecoveryMiddleware(),
httpc.LoggingMiddleware(log.Printf),
},
UserAgent: "my-app/1.0",
Headers: map[string]string{"X-App": "my-app"},
FollowRedirects: true,
MaxRedirects: 10,
}
client, _ := httpc.New(cfg)Что дальше
- Промежуточное ПО API - полный справочник промежуточного ПО
- Повторные попытки и отказоустойчивость - руководство по стратегии повторов
- Обзор безопасности - практики безопасности промежуточного ПО аудита