feat: bootstrap reusable GoOmni framework
This commit is contained in:
82
middleware/middleware.go
Normal file
82
middleware/middleware.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"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. The network adapter does not need a Gin-specific recovery
|
||||
// hook for this behavior.
|
||||
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 {
|
||||
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", clientIP(ctx.Request()),
|
||||
)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clientIP(request *http.Request) string {
|
||||
if request == nil {
|
||||
return ""
|
||||
}
|
||||
if value := request.Header.Get("X-Forwarded-For"); value != "" {
|
||||
return value
|
||||
}
|
||||
return request.RemoteAddr
|
||||
}
|
||||
Reference in New Issue
Block a user