Skip to content

Промежуточное ПО

Обзор архитектуры

Эта страница — справочник по встроенному middleware. Общая архитектура конвейера Handler, принципы луковой модели и написание собственного middleware рассматриваются в Конвейер обработчиков / Handler и цепочка middleware.

HTTPC использует архитектуру промежуточного ПО по модели лука, оборачивая логику обработки запросов через MiddlewareFunc.

go
type MiddlewareFunc func(Handler) Handler
type Handler func(ctx context.Context, req RequestMutator) (ResponseMutator, error)

Промежуточное ПО настраивается в MiddlewareConfig.Middlewares, выполняется в порядке добавления:

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.RecoveryMiddleware(),
            httpc.LoggingMiddleware(log.Printf),
            httpc.RequestIDMiddleware("X-Request-ID", nil),
        },
    },
})

Chain

go
func Chain(middlewares ...MiddlewareFunc) MiddlewareFunc

Объединяет несколько промежуточных ПО в одно. Выполняются в порядке передачи, после обработки последним вызывается финальный Handler.

go
combined := httpc.Chain(
    httpc.RecoveryMiddleware(),
    httpc.LoggingMiddleware(log.Printf),
)

Встроенное промежуточное ПО

RecoveryMiddleware

go
func RecoveryMiddleware() MiddlewareFunc

Промежуточное ПО восстановления после panic. Перехватывает panic в цепочке обработки, преобразуя его в error с информацией о стеке вызовов.

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.RecoveryMiddleware(),
        },
    },
})

LoggingMiddleware

go
func LoggingMiddleware(log func(format string, args ...any)) MiddlewareFunc

Промежуточное ПО логирования запросов. Записывает метод, URL, код состояния и время выполнения. URL автоматически маскируется (удаляется информация об учётных данных).

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.LoggingMiddleware(log.Printf),
        },
    },
})
// Пример вывода: GET https://api.example.com/data -> 200 (125ms)

RequestIDMiddleware

go
func RequestIDMiddleware(headerName string, generator func() string) MiddlewareFunc

Добавляет уникальный ID каждому запросу. По умолчанию использует crypto/rand для генерации 32-символьного шестнадцатеричного ID; если в запросе уже есть заголовок с тем же именем, исходное значение сохраняется и не перезаписывается.

ПараметрОписание
headerNameИмя заголовка, например "X-Request-ID"
generatorПользовательская функция генерации ID, передайте nil для использования генератора по умолчанию
go
// С генератором по умолчанию
middleware := httpc.RequestIDMiddleware("X-Request-ID", nil)

// С пользовательским генератором
middleware := httpc.RequestIDMiddleware("X-Request-ID", func() string {
    return uuid.New().String()
})

TIP

Генератор по умолчанию использует crypto/rand, генерируемые ID непредсказуемы, подходят для сценариев с повышенными требованиями к безопасности.

TimeoutMiddleware

go
func TimeoutMiddleware(timeout time.Duration) MiddlewareFunc

Управление таймаутом на уровне промежуточного ПО. Срабатывает до встроенного таймаута клиента, при истечении отменяет контекст и возвращает ошибку.

Не используйте для Download или потоковых запросов

defer cancel() в TimeoutMiddleware срабатывает сразу после возврата обработчика (т.е. после получения заголовков ответа), поэтому для запросов Download или WithStreamBody контекст отменяется до чтения тела ответа, что проявляется как ошибка «context canceled». Для потоковых сценариев и загрузок используйте опцию WithTimeout.

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.TimeoutMiddleware(10 * time.Second),
        },
    },
})

HeaderMiddleware

go
func HeaderMiddleware(headers map[string]string) MiddlewareFunc

Добавляет статические заголовки каждому запросу. Безопасность заголовков проверяется при создании (защита от CRLF-инъекций); при конфликте с уже существующими заголовками с тем же именем они будут перезаписаны.

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.HeaderMiddleware(map[string]string{
                "X-API-Version": "v2",
                "X-Client":      "myapp/1.0",
            }),
        },
    },
})

MetricsMiddleware

go
func MetricsMiddleware(onMetrics func(method, url string, statusCode int, duration time.Duration, err error)) MiddlewareFunc

Промежуточное ПО сбора метрик. Вызывает обратный вызов после каждого запроса, передавая метод, URL, код состояния, время выполнения и информацию об ошибке.

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.MetricsMiddleware(func(method, url string, status int, d time.Duration, err error) {
                metrics.Record(method, status, d, err)
            }),
        },
    },
})

AuditMiddleware

go
func AuditMiddleware(onAudit func(event AuditEvent)) MiddlewareFunc

Промежуточное ПО безопасности для аудита, подходит для финансовых, медицинских, государственных и других сценариях с требованиями соответствия. По умолчанию записывает метаданные запроса/ответа (метод, URL, код состояния, длительность, повторные попытки и т. д.), URL автоматически маскируется; для записи полных заголовков используйте AuditMiddlewareWithConfig с IncludeHeaders: true.

go
client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.AuditMiddleware(func(event httpc.AuditEvent) {
                log.Printf("[AUDIT] %s %s -> %d (%v) user=%s ip=%s",
                    event.Method, event.URL, event.StatusCode,
                    event.Duration, event.UserID, event.SourceIP)
            }),
        },
    },
})

AuditMiddlewareWithConfig

go
func AuditMiddlewareWithConfig(onAudit func(event AuditEvent), config *AuditMiddlewareConfig) MiddlewareFunc

Промежуточное ПО безопасности для аудита с конфигурацией.

go
config := &httpc.AuditMiddlewareConfig{
    Format:         "json",
    IncludeHeaders: true,
    MaskHeaders:    []string{"Authorization", "Cookie"},
    SanitizeError:  true,
}

client, _ := httpc.New(&httpc.Config{
    Middleware: &httpc.MiddlewareConfig{
        Middlewares: []httpc.MiddlewareFunc{
            httpc.AuditMiddlewareWithConfig(func(event httpc.AuditEvent) {
                data, _ := json.Marshal(event)
                auditLog.Write(data)
            }, config),
        },
    },
})

Типы аудита

AuditEvent

go
type AuditEvent struct {
    Timestamp     time.Time           `json:"timestamp"`
    Method        string              `json:"method"`
    URL           string              `json:"url"`              // Маскировано (учётные данные удалены)
    StatusCode    int                 `json:"statusCode"`
    Duration      time.Duration       `json:"duration"`
    Attempts      int                 `json:"attempts"`
    Error         error               `json:"error,omitempty"`
    SourceIP      string              `json:"sourceIP,omitempty"`
    UserID        string              `json:"userID,omitempty"`
    RedirectChain []string            `json:"redirectChain,omitempty"`
    ReqHeaders    map[string][]string `json:"reqHeaders,omitempty"`
    RespHeaders   map[string][]string `json:"respHeaders,omitempty"`
}

Событие безопасности аудита.

MarshalJSON

go
func (e AuditEvent) MarshalJSON() ([]byte, error)

Пользовательская сериализация JSON, обрабатывает два специальных поля:

ПолеПравило преобразования
DurationДобавляется durationMs (целое число миллисекунд), сохраняется исходное поле duration (наносекунды)
ErrorПреобразуется в error (строка сообщения об ошибке), при nil опускается
go
event := httpc.AuditEvent{
    Method:    "GET",
    URL:       "https://api.example.com/data",
    Duration:  150 * time.Millisecond,
    StatusCode: 200,
}
data, _ := json.Marshal(event)
// {"timestamp":"...","method":"GET","url":"...","statusCode":200,"duration":150000000,"attempts":0,"durationMs":150}

AuditMiddlewareConfig

go
type AuditMiddlewareConfig struct {
    Format         string   // "text" (по умолчанию) или "json"
    IncludeHeaders bool     // Включать ли заголовки запроса/ответа
    MaskHeaders    []string // Имена заголовков для маскировки
    SanitizeError  bool     // Маскировать ли информацию об ошибках
}
ПолеЗначение по умолчаниюОписание
Format"text"Формат вывода
IncludeHeadersfalseЗаписывать ли заголовки
MaskHeaders["Authorization", "Cookie", ...]Стандартный список конфиденциальных заголовков
SanitizeErrortrueИнформация об ошибках заменяется на [sanitized]

DefaultAuditMiddlewareConfig

go
func DefaultAuditMiddlewareConfig() *AuditMiddlewareConfig

Возвращает конфигурацию аудита по умолчанию.

Ключи контекста аудита

Передача информации аудита через контекст запроса:

go
// Установка исходного IP
ctx = context.WithValue(ctx, httpc.SourceIPKey, "192.168.1.1")

// Установка идентификатора пользователя
ctx = context.WithValue(ctx, httpc.UserIDKey, "user-123")

result, err := client.Request(ctx, "GET", url)
КонстантаТипОписание
SourceIPKeyauditContextKeyКлюч контекста исходного IP
UserIDKeyauditContextKeyКлюч контекста идентификатора пользователя

См. также