Request ID Propagation Middleware
Validate or generate a request ID, echo it in the response header, and expose it through a private context key for logging.
Code
package main
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
)
type requestIDKey struct{}
func validRequestID(id string) bool {
if len(id) == 0 || len(id) > 128 {
return false
}
for _, char := range id {
switch {
case char >= 'a' && char <= 'z':
case char >= 'A' && char <= 'Z':
case char >= '0' && char <= '9':
case char == '-' || char == '_':
default:
return false
}
}
return true
}
func RequestIDMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("X-Request-ID")
if !validRequestID(id) {
buffer := make([]byte, 8)
if _, err := rand.Read(buffer); err != nil {
http.Error(w, "cannot create request id", http.StatusInternalServerError)
return
}
id = hex.EncodeToString(buffer)
}
w.Header().Set("X-Request-ID", id)
ctx := context.WithValue(r.Context(), requestIDKey{}, id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func RequestIDFrom(ctx context.Context) string {
id, _ := ctx.Value(requestIDKey{}).(string)
return id
}
func main() {
handler := RequestIDMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "request", RequestIDFrom(r.Context()))
}))
http.Handle("/", handler)
_ = http.ListenAndServe(":8080", nil)
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.