72 lines
2.1 KiB
Go
72 lines
2.1 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
apierrors "git.yuntuo.hk/k2safe/go-omni/errors"
|
|
"git.yuntuo.hk/k2safe/go-omni/httpx"
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
)
|
|
|
|
// RequestIDKey is the value key used by the request ID middleware.
|
|
const RequestIDKey = "go-omni.requestId"
|
|
|
|
// RequestID reads or creates a request ID and returns it in the response
|
|
// header. It is a transport-neutral GoOmni middleware.
|
|
func RequestID(header string) httpx.Middleware {
|
|
return func(next httpx.Handler) httpx.Handler {
|
|
return func(ctx *httpx.Context) error {
|
|
requestID := ctx.Header(header)
|
|
if requestID == "" {
|
|
requestID = newRequestID()
|
|
}
|
|
ctx.Set(RequestIDKey, requestID)
|
|
ctx.SetResponseHeader(header, requestID)
|
|
ctx.WithContext(logx.WithRequestID(ctx.Context(), requestID))
|
|
return next(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recovery converts panics from handlers and middleware into the standard
|
|
// internal error. Recovery belongs to GoOmni's transport-neutral middleware
|
|
// rather than a router-specific hook.
|
|
func Recovery() httpx.Middleware {
|
|
return func(next httpx.Handler) httpx.Handler {
|
|
return func(ctx *httpx.Context) (err error) {
|
|
defer func() {
|
|
if recovered := recover(); recovered != nil {
|
|
err = apierrors.Internal(fmt.Errorf("panic recovered: %v", recovered))
|
|
}
|
|
}()
|
|
return next(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
// AccessLog records one structured event after each request. It deliberately
|
|
// lives in GoOmni instead of using a router-specific logger so business
|
|
// applications get the same log format with every transport adapter.
|
|
func AccessLog() httpx.Middleware {
|
|
return func(next httpx.Handler) httpx.Handler {
|
|
return func(ctx *httpx.Context) error {
|
|
started := time.Now()
|
|
err := next(ctx)
|
|
status := ctx.Status()
|
|
if err != nil && !ctx.Responded() {
|
|
status = apierrors.From(err).Status
|
|
}
|
|
logx.InfoContext(ctx.Context(), "http.request",
|
|
"method", ctx.Request().Method,
|
|
"path", ctx.Request().URL.Path,
|
|
"route", httpx.RoutePattern(ctx.Context()),
|
|
"status", status,
|
|
"durationMs", time.Since(started).Seconds()*1000,
|
|
"clientIp", ctx.ClientIP(),
|
|
)
|
|
return err
|
|
}
|
|
}
|
|
}
|