middleware/ 提供基于 Gin 的生产级中间件集合,并额外兼容 Chi 风格 func(http.Handler) http.Handler 中间件。
该目录内既包含框架默认启用的核心中间件,也包含安全、性能、业务与 Chi 生态移植组件。默认 gin.Default() 会预装 RequestID、Recovery、Logger;其余中间件按需在路由或分组上启用。
基于实际实现整理:
requestid.go、recovery.go、logger.go、cors.go、secure.go、ratelimit.go、signature.go、cache.go、compress.go、timeout.go、idempotent.go、interceptor.go、registry.go,以及多组 Chi 风格兼容件。
框架 gin.Default() 在 engine.go 中默认注册:
RequestID():注入/透传 X-Request-IDRecovery():捕获 panic,避免进程崩溃Logger():记录请求方法、路径、状态码、耗时、客户端 IP| 分类 | 中间件 | 作用 |
|---|---|---|
| Core | RequestID、Recovery、Logger、OTel | 请求追踪、崩溃恢复、访问日志、OpenTelemetry 接入 |
| Security | CORS、Secure、RateLimit、SignatureVerify | 跨域、安全响应头、访问限流、签名验签 |
| Performance | Cache、Compress、Timeout | 响应缓存、压缩、超时控制 |
| Business | Idempotent | 幂等控制、防重复提交 |
| Chain / Extensibility | Interceptor、Registry、Maybe | 请求/响应拦截、注册表、条件执行 |
| Chi compatibility | Throttle、RealIP、NoCache、URLFormat、Sunset、RouteHeaders、ValidateParam | 从 Chi 设计迁移或兼容的中间件能力 |
| Other built-ins | ETag、CircuitBreaker | 条件缓存、熔断保护 |
RequestID() 读取请求头 X-Request-ID;若不存在则生成 UUID,并同时写入:
request_idX-Request-IDr.Use(middleware.RequestID())
r.GET("/ping", func(c *gin.Context) {
requestID, _ := c.Get("request_id")
c.JSON(200, gin.H{"request_id": requestID})
})
适用场景:链路追踪、日志关联、错误排查。
Recovery() 使用 os.Stderr 输出 panic 日志;RecoveryWithWriter(out) 可自定义日志输出位置。
实现特性:
recover() 捕获 panicbroken pipe / connection reset 做特殊处理500r.Use(middleware.Recovery())
// 自定义日志输出
r.Use(middleware.RecoveryWithWriter(os.Stdout))
适用场景:生产环境防止单请求 panic 造成服务崩溃。
Logger() 使用标准库 log.Logger 输出访问日志,记录:
r.Use(middleware.Logger())
适用场景:基础访问审计、延迟观测。
OTel(service, opts...) 基于官方 otelgin 中间件为请求接入 OpenTelemetry 追踪与指标。
r.Use(middleware.OTel("order-api"))
配合增强 Context,可以在处理器中直接读取:
c.TraceID()c.SpanID()适用场景:链路追踪、分布式排障、指标采集。
CORS(config ...CORSConfig) 配置跨域响应头。未传配置时使用 DefaultCORSConfig()。
默认值包括:
AllowOrigins: ["*"]AllowMethods: GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONSAllowHeaders: Origin/Content-Length/Content-Type/AuthorizationMaxAge: 12h对 OPTIONS 请求会直接返回 204 No Content。
r.Use(middleware.CORS())
r.Use(middleware.CORS(middleware.CORSConfig{
AllowOrigins: []string{"https://app.example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Content-Type", "Authorization", "X-Request-ID"},
ExposeHeaders: []string{"X-Request-ID"},
MaxAge: 3600,
AllowCredentials: true,
}))
Secure() 注入一组安全响应头:
X-Frame-Options: SAMEORIGINX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockStrict-Transport-Security: max-age=31536000; includeSubDomainsr.Use(middleware.Secure())
RateLimit(config ...RateLimitConfig) 为每个客户端 IP 创建独立 rate.Limiter。
默认配置:
RequestsPerSecond: 10Burst: 20超限后直接返回 429 Too Many Requests。内部会周期清理长期未访问的访客桶。
r.Use(middleware.RateLimit())
r.Use(middleware.RateLimit(middleware.RateLimitConfig{
RequestsPerSecond: 20,
Burst: 40,
}))
SignatureVerify(opts ...SignatureOption) 对请求进行 HMAC 验签,内置防重放保护。
要求请求头:
X-TimestampX-NonceX-Signature默认行为:
HMAC-SHA256300sNonceStore:内存实现10MBr.Use(middleware.SignatureVerify(
middleware.WithSignatureSecret("top-secret"),
middleware.WithSignatureExpiry(300),
middleware.WithSignatureAlgorithm("HMAC-SHA256"),
middleware.WithSignatureHeaders("X-Device-ID", "X-Tenant-ID"),
))
客户端可使用辅助函数生成签名:
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
nonce, err := middleware.GenerateNonce()
if err != nil {
return err
}
signature := middleware.GenerateSignature(
"POST",
"/api/orders",
`{"amount":100}`,
timestamp,
nonce,
"top-secret",
"HMAC-SHA256",
map[string]string{"X-Device-ID": "mobile"},
)
Cache(duration, opts...) 仅缓存 GET 和 HEAD 请求的成功响应(2xx)。
实现要点:
pkg/cache.NewMemoryCache()method:path:query 的 SHA-256 哈希X-Cache: HITX-Cache: MISSgob 序列化存储r.GET("/articles/:id",
middleware.Cache(5*time.Minute),
func(c *gin.Context) {
c.JSON(200, gin.H{"id": c.Param("id")})
},
)
自定义缓存存储、Key、响应头:
store := cache.NewMemoryCache()
r.GET("/profile",
middleware.Cache(30*time.Second,
middleware.WithCacheStore(store),
middleware.WithCacheControl("public, max-age=30"),
middleware.WithCacheVary("Authorization"),
middleware.WithCacheKey(func(c *gin.Context) string {
return "profile:" + c.GetHeader("Authorization")
}),
),
handler,
)
条件缓存:
r.GET("/feed",
middleware.CacheIf(func(c *gin.Context) bool {
return c.Query("preview") == ""
}, time.Minute),
handler,
)
Compress(opts ...CompressOption) 根据 Accept-Encoding 自动选择压缩算法。
当前实现支持:
gzip(默认优先)deflatebr默认只压缩常见文本与 JSON/XML MIME,且响应体长度至少 1024 字节。
r.Use(middleware.Compress())
r.Use(middleware.Compress(
middleware.WithCompressAlgorithm("br"),
middleware.WithCompressMinLength(512),
middleware.WithCompressTypes("application/json", "text/html"),
))
Timeout(d) 将请求上下文包装为 context.WithTimeout,并在超时后返回 408 Request Timeout(仅当处理器未写响应时)。
实现方式:
Contextc.Next()r.Use(middleware.Timeout(2 * time.Second))
注意:它控制的是请求处理链,而不是底层 socket/read/write timeout;后者仍应通过 Engine 配置管理。
Idempotent(opts ...IdempotentOption) 用请求头 Idempotency-Key 作为默认幂等键,缓存首次响应,后续相同 key 直接重放缓存结果。
默认行为:
5mMemoryIdempotentStoreIdempotency-Keyr.POST("/payments",
middleware.Idempotent(),
func(c *gin.Context) {
c.JSON(201, gin.H{"status": "created"})
},
)
自定义 TTL / Store / Key / Skip 逻辑:
r.POST("/orders",
middleware.Idempotent(
middleware.WithIdempotentTTL(10*time.Minute),
middleware.WithIdempotentKeyFunc(func(c *gin.Context) string {
return c.GetHeader("X-Order-Key")
}),
middleware.WithIdempotentSkipFunc(func(c *gin.Context) bool {
return c.Query("dry_run") == "true"
}),
),
createOrder,
)
Interceptor(config InterceptorConfig) 同时支持:
OnRequest: 进入业务前校验/改写OnResponse: 捕获响应体后再处理请求拦截失败返回 400;响应处理失败返回 500。
r.Use(middleware.Interceptor(middleware.InterceptorConfig{
OnRequest: func(c *gin.Context) error {
if c.GetHeader("X-App-ID") == "" {
return errors.New("missing X-App-ID")
}
return nil
},
OnResponse: func(c *gin.Context, body []byte) ([]byte, error) {
return bytes.ToUpper(body), nil
},
}))
Maybe(mw, maybeFn) 在满足条件时执行目标中间件,否则直接跳过。
r.Use(middleware.Maybe(middleware.Logger(), func(c *gin.Context) bool {
return strings.HasPrefix(c.Request.URL.Path, "/debug")
}))
Registry 用于集中注册、启停与排序中间件。内建注册项包括:
recoveryrequest_idloggercorsratelimittimeoutsecurecircuit_breaker默认启用:recovery、request_id、logger。
reg := middleware.NewRegistry()
reg.Enable("cors", "secure")
reg.Disable("logger")
for _, mw := range reg.GetChain() {
r.Use(mw)
}
框架对 Chi / 标准库风格中间件的兼容入口在 router.go 的 Router.Use():
gin.HandlerFuncfunc(*gin.Context)HandlerFuncfunc(http.Handler) http.Handler(Chi 风格)其中 gin_compat.go 导出了 HTTPMiddleware 类型别名,便于声明标准 HTTP middleware。
Chi 中间件通过 adaptHTTPMiddleware 适配进 Gin 链时,框架会:
nextHandlernext.ServeHTTPAbort()next,也自动 Abort(),避免 Gin 继续向下执行这保证了 Chi 与 Gin 两种链模型在行为上的一致性。
import chimw "github.com/go-chi/chi/v5/middleware"
r.Use(chimw.RequestID)
r.Use(chimw.Logger)
r.Use(chimw.Recoverer)
nextnextAbort() 语义时,优先使用 Gin 风格中间件| 中间件 | 说明 |
|---|---|
Throttle / ThrottleBacklog | 并发请求节流与积压队列 |
RealIP | 解析 X-Forwarded-For / X-Real-IP |
NoCache | 禁用缓存响应头 |
URLFormat | 解析 .json / .xml 等 URL 扩展 |
Sunset | RFC 8594 API 废弃通知 |
RouteHeaders | 按请求头模式路由中间件 |
ValidateParam | 路由参数正则校验 |
示例:
api := e.Router().Group("/api")
api.Use(middleware.RealIP())
api.Use(middleware.NoCache())
api.Use(middleware.Throttle(100))
api.Use(middleware.URLFormat())
r.Use(middleware.Throttle(100))
r.Use(middleware.ThrottleBacklog(100, 50, 30*time.Second))
r.Use(middleware.RealIP())
r.GET("/whoami", func(c *gin.Context) {
c.JSON(200, gin.H{"ip": middleware.GetRealIP(c)})
})
r.Use(middleware.NoCache())
r.Use(middleware.URLFormat())
r.GET("/articles/*path", func(c *gin.Context) {
switch middleware.GetURLFormat(c) {
case "xml":
c.XML(200, article)
default:
c.JSON(200, article)
}
})
r.Use(middleware.Sunset(
time.Date(2026, 12, 31, 23, 59, 59, 0, time.UTC),
"<https://api.example.com/v2>; rel=\"successor-version\"",
))
r.Use(middleware.RouteHeaders().
Route("Origin", "https://app.example.com", middleware.CORS()).
RouteDefault(middleware.NoCache()).
Handler())
r.GET("/users/:id",
middleware.ValidateParam("id", middleware.PatternNumeric),
getUser,
)
适合纯 gin.HandlerFunc 场景。
func AuditTrail() gin.HandlerFunc {
return func(c *gin.Context) {
startedAt := time.Now()
c.Next()
log.Printf("%s %s %v", c.Request.Method, c.Request.URL.Path, time.Since(startedAt))
}
}
r.Use(AuditTrail())
若使用框架的增强路由器,可直接传入 func(*gin.Context) 或框架 HandlerFunc;Router.Use() 会自动适配。
router := e.Router()
router.Use(func(c *gin.Context) {
c.Set("tenant_id", c.GetHeader("X-Tenant-ID"))
c.Next()
})
适合复用现有 net/http 或 Chi 中间件资产。
func HeaderAudit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Audit", "on")
next.ServeHTTP(w, r)
})
}
router.Use(HeaderAudit)
若中间件需要被统一启停、排序、延迟构造,可注册到 Registry。
reg := middleware.NewRegistry()
reg.Register(&middleware.Middleware{
Name: "audit",
Description: "记录审计头",
Factory: AuditTrail,
Order: 25,
Enabled: true,
})
建议:
Abort() 或停止调用 nextSignatureVerify 应早于业务处理middleware/interceptor.go:请求/响应双向拦截middleware/registry.go:中间件注册、启停、排序router.go:Router.Use() 智能适配与 Chi middleware 桥接gin_compat.go:兼容导出与 HTTPMiddleware 类型别名engine.go:默认核心链注册入口