/
t3
/
token-server
Обзор
Документация
Войти
/
t3
/
token-server
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
main
internal/api/middleware.go
96 строк
3 KB
Ivan Shibkikh
auth: phase 3
25 июл 2026, 22:52
25 июл 2026, 22:52
a53c429
Код
Авторство
О чём код?
package api import ( "context" "log/slog" "net/http" "runtime/debug" "time" "github.com/google/uuid" "gitverse.ru/t3/sts/internal/requestid" ) // requestIDFromContext is a small helper used by error responses. func requestIDFromContext(r *http.Request) string { return requestid.FromContext(r.Context()) } // WithRequestID stores the request id in context (alias for readability). func WithRequestID(ctx context.Context, id string) context.Context { return requestid.WithContext(ctx, id) } // RequestIDMiddleware generates a fresh request/correlation ID for each // incoming request, echoes it in the X-Request-ID response header, and stores // it in context for downstream handlers and logging. func RequestIDMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rid := r.Header.Get("X-Request-ID") if rid == "" { rid = uuid.NewString() } w.Header().Set("X-Request-ID", rid) ctx := WithRequestID(r.Context(), rid) next.ServeHTTP(w, r.WithContext(ctx)) }) } type statusRecorder struct { http.ResponseWriter status int written int64 } func (r *statusRecorder) WriteHeader(code int) { r.status = code r.ResponseWriter.WriteHeader(code) } func (r *statusRecorder) Write(b []byte) (int, error) { if r.status == 0 { r.status = http.StatusOK } n, err := r.ResponseWriter.Write(b) r.written += int64(n) return n, err } // LoggingMiddleware logs each request with method, path, status, size, duration. func LoggingMiddleware(logger *slog.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() sr := &statusRecorder{ResponseWriter: w, status: http.StatusOK} next.ServeHTTP(sr, r) logger.Info("request", "method", r.Method, "path", r.URL.Path, "status", sr.status, "bytes", sr.written, "duration", time.Since(start).String(), "request_id", requestIDFromContext(r), ) }) } } // RecoveryMiddleware catches panics, logs the stack trace and returns 500. func RecoveryMiddleware(logger *slog.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if rec := recover(); rec != nil { logger.Error("panic recovered", "error", rec, "request_id", requestIDFromContext(r), "stack", string(debug.Stack()), ) writeRequestIDError(w, r, http.StatusInternalServerError, "internal_error", "internal server error") } }() next.ServeHTTP(w, r) }) } }