feat: backend service
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// Package requestid carries a per-request identifier across the gin handler
|
||||
// chain.
|
||||
//
|
||||
// The middleware reads the inbound `X-Request-ID` header, generates a UUIDv4
|
||||
// when absent, stores the value on the gin context, and reflects it on the
|
||||
// response. Downstream code retrieves the identifier through FromContext.
|
||||
package requestid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Header is the canonical case-correct header name carrying the request id.
|
||||
const Header = "X-Request-ID"
|
||||
|
||||
// ginContextKey is the gin.Context key under which the resolved request id is
|
||||
// stored. The value is a string. The key is exported in lowercase form so that
|
||||
// it never collides with handler-level keys; consumers should prefer
|
||||
// FromContext rather than reading the gin context directly.
|
||||
const ginContextKey = "backend.request_id"
|
||||
|
||||
// requestIDContextKey is the unexported context.Context key used when the
|
||||
// resolved request id is propagated outside gin (background goroutines,
|
||||
// downstream client calls). The unexported value type prevents accidental
|
||||
// collisions across packages.
|
||||
type requestIDContextKey struct{}
|
||||
|
||||
// Middleware returns the gin middleware that resolves and propagates the
|
||||
// request id.
|
||||
func Middleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
requestID := strings.TrimSpace(c.GetHeader(Header))
|
||||
if requestID == "" {
|
||||
requestID = uuid.NewString()
|
||||
}
|
||||
|
||||
c.Set(ginContextKey, requestID)
|
||||
c.Writer.Header().Set(Header, requestID)
|
||||
c.Request = c.Request.WithContext(WithValue(c.Request.Context(), requestID))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// FromContext returns the request id stored on ctx by Middleware. The boolean
|
||||
// reports whether an id was found. Consumers must always check the boolean
|
||||
// before using the returned string.
|
||||
func FromContext(ctx context.Context) (string, bool) {
|
||||
if ctx == nil {
|
||||
return "", false
|
||||
}
|
||||
value, ok := ctx.Value(requestIDContextKey{}).(string)
|
||||
if !ok || value == "" {
|
||||
return "", false
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
|
||||
// FromGin returns the request id stored on the gin context by Middleware. The
|
||||
// boolean reports whether an id was found.
|
||||
func FromGin(c *gin.Context) (string, bool) {
|
||||
if c == nil {
|
||||
return "", false
|
||||
}
|
||||
raw, ok := c.Get(ginContextKey)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
value, ok := raw.(string)
|
||||
if !ok || value == "" {
|
||||
return "", false
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
|
||||
// WithValue stores requestID on ctx under the package-private context key.
|
||||
// Exposed primarily for tests that build a context outside the middleware.
|
||||
func WithValue(ctx context.Context, requestID string) context.Context {
|
||||
return context.WithValue(ctx, requestIDContextKey{}, requestID)
|
||||
}
|
||||
Reference in New Issue
Block a user