ミドルウェアチェーン
オニオンモデル
HTTPC のミドルウェアはオニオンモデルを採用しています。リクエストは外から内へ、レスポンスは内から外へ流れます:
text
リクエスト → Recovery → Logging → RequestID → Handler
↓
レスポンス ← Recovery ← Logging ← RequestID ← Responsego
cfg := httpc.DefaultConfig()
cfg.Middleware.Middlewares = []httpc.MiddlewareFunc{
httpc.RecoveryMiddleware(), // 最外層:panic リカバリ
httpc.LoggingMiddleware(&httpc.LoggingConfig{LogFunc: log.Printf}), // 第 2 層:ログ記録
httpc.RequestIDMiddleware(httpc.DefaultRequestIDConfig()), // 最内層:リクエスト ID
}
client, err := httpc.New(cfg)
if err != nil {
log.Fatal(err)
}
defer client.Close()内蔵ミドルウェア
RecoveryMiddleware
panic リカバリ。プロセスのクラッシュを防止:
go
httpc.RecoveryMiddleware()LoggingMiddleware
リクエスト/レスポンスログ。URL は自動的にマスク:
go
httpc.LoggingMiddleware(&httpc.LoggingConfig{LogFunc: func(format string, args ...any) {
log.Printf("[HTTP] "+format, args...)
}})
// 出力例:[HTTP] GET https://api.example.com/data -> 200 (150ms)(ステータスコードと所要時間は実測値、固定ではありません)RequestIDMiddleware
各リクエストにユニーク ID を追加。crypto/rand で生成:
go
httpc.RequestIDMiddleware(httpc.DefaultRequestIDConfig()) // デフォルト 32 文字 hex
// カスタムジェネレーター
httpc.RequestIDMiddleware(&httpc.RequestIDConfig{
HeaderName: "X-Request-ID",
Generator: func() string {
return uuid.New().String()
},
})TimeoutMiddleware
ミドルウェア層のタイムアウト。クライアントタイムアウトより前に強制実行:
go
httpc.TimeoutMiddleware(&httpc.TimeoutMiddlewareConfig{Duration: 30 * time.Second})Download やストリーミングリクエストには使用しないでください
TimeoutMiddleware の defer cancel() は、ハンドラーが戻った(レスポンスヘッダーを受信した)直後に発火します。Download や WithStreamBody リクエストでは、レスポンスボディを読み取る前にコンテキストがキャンセルされ、「context canceled」エラーとして現れます。ストリーミング/ダウンロードのシナリオでは WithTimeout オプションを使用してください。
HeaderMiddleware
全リクエストに静的ヘッダーを追加:
go
httpc.HeaderMiddleware(&httpc.HeaderConfig{Headers: map[string]string{
"X-App-Version": "1.0.0",
"X-Platform": "server",
}})MetricsMiddleware
リクエストメトリクスを収集:
go
httpc.MetricsMiddleware(&httpc.MetricsConfig{OnMetrics: 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
セキュリティ監査。金融、医療などのコンプライアンスシナリオに使用:
go
auditCfg := httpc.DefaultAuditConfig()
auditCfg.OnAudit = func(event httpc.AuditEvent) {
log.Printf("[AUDIT] %s %s -> %d (%v)",
event.Method, event.URL, event.StatusCode, event.Duration)
}
httpc.AuditMiddleware(auditCfg)監査オプションの設定
DefaultAuditConfig() でデフォルト設定を取得してからフィールドを変更することで、出力フォーマット、ヘッダー記録、マスキングを制御できます:
go
auditCfg := httpc.DefaultAuditConfig()
auditCfg.Format = "json"
auditCfg.IncludeHeaders = true
auditCfg.MaskHeaders = []string{"Authorization", "Cookie"}
auditCfg.SanitizeError = true
auditCfg.OnAudit = func(event httpc.AuditEvent) {
data, err := json.Marshal(event)
if err != nil {
log.Println("監査イベントのシリアライズ失敗:", err)
return
}
log.Println(string(data))
}
httpc.AuditMiddleware(auditCfg)監査イベントはコンテキストから SourceIP と UserID の抽出をサポートします:
go
ctx := context.WithValue(context.Background(), httpc.SourceIPKey, "192.168.1.1")
ctx = context.WithValue(ctx, httpc.UserIDKey, "user-123")手動チェーン組み合わせ
Chain 関数でミドルウェアを組み合わせます:
go
middleware := httpc.Chain(
httpc.RecoveryMiddleware(),
httpc.LoggingMiddleware(&httpc.LoggingConfig{LogFunc: log.Printf}),
httpc.RequestIDMiddleware(httpc.DefaultRequestIDConfig()),
)
cfg := httpc.DefaultConfig()
cfg.Middleware.Middlewares = []httpc.MiddlewareFunc{middleware}カスタムミドルウェア
go
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
}
}
}ショートサーキットミドルウェア
go
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
}
}
}ミドルウェア設定
go
cfg := httpc.DefaultConfig()
cfg.Middleware.Middlewares = []httpc.MiddlewareFunc{
httpc.RecoveryMiddleware(),
httpc.LoggingMiddleware(&httpc.LoggingConfig{LogFunc: log.Printf}),
}
cfg.Defaults.UserAgent = "my-app/1.0"
cfg.Defaults.Headers = map[string]string{"X-App": "my-app"}
cfg.Defaults.FollowRedirects = true
cfg.Defaults.MaxRedirects = 10
client, err := httpc.New(cfg)
if err != nil {
log.Fatal(err)
}
defer client.Close()次のステップ
- ミドルウェア API - 完全なミドルウェアリファレンス
- リトライとフォールトトレランス - リトライポリシーガイド
- セキュリティ概要 - 監査ミドルウェアのセキュリティプラクティス