中间件在Web开发中扮演着管道过滤器的角色,就像工厂流水线上的质检员。当一个HTTP请求到达服务器时,它会依次通过注册的中间件链,每个中间件都有机会对请求进行检查、修改或拦截。在Gin框架中,中间件的核心是实现了gin.HandlerFunc接口的函数:
go复制type HandlerFunc func(*Context)
这个简单的函数签名背后蕴含着强大的扩展能力。每个中间件接收一个*gin.Context参数,这个上下文对象贯穿整个请求生命周期,包含了请求和响应的所有信息。中间件通过调用c.Next()将控制权交给下一个中间件,形成典型的洋葱模型处理流程。
关键理解:中间件不是Gin特有的概念,但Gin通过简洁的API设计使其使用变得异常直观。这种设计模式源自函数式编程中的高阶函数思想。
让我们通过一个具体例子理解中间件的执行顺序:
go复制func middleware1(c *gin.Context) {
fmt.Println("进入 middleware1")
c.Next()
fmt.Println("离开 middleware1")
}
func middleware2(c *gin.Context) {
fmt.Println("进入 middleware2")
c.Next()
fmt.Println("离开 middleware2")
}
func main() {
r := gin.Default()
r.Use(middleware1, middleware2)
r.GET("/test", func(c *gin.Context) {
fmt.Println("处理请求")
c.String(200, "OK")
})
r.Run(":8080")
}
当访问/test时,控制台输出将是:
code复制进入 middleware1
进入 middleware2
处理请求
离开 middleware2
离开 middleware1
这个例子清晰展示了中间件的"先进后出"特性,就像栈的操作顺序。理解这个执行模型对编写正确的中间件至关重要。
Logger中间件是Gin默认提供的请求日志记录工具,但它的能力远不止打印基础信息。通过自定义配置,我们可以获得更强大的日志功能:
go复制func setupRouter() *gin.Engine {
r := gin.New()
// 自定义Logger配置
r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))
return r
}
这种自定义格式可以输出类似Apache/Nginx的访问日志格式。在实际生产环境中,我们通常会:
Recovery中间件是Gin应用的保险丝,它能捕获处理过程中抛出的panic,防止单个请求的错误导致整个服务崩溃。但默认配置可能不足以应对生产环境需求,我们需要增强其能力:
go复制func customRecovery() gin.HandlerFunc {
return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
if err, ok := recovered.(error); ok {
// 记录完整错误堆栈
log.Printf("Panic recovered: %+v\n", err)
// 发送告警通知
notifyTeam(err)
// 返回标准化错误响应
c.JSON(500, gin.H{
"code": 50000,
"message": "Internal Server Error",
"request_id": c.GetString("X-Request-ID"),
})
}
c.AbortWithStatus(500)
})
}
在实际项目中,我们还需要注意:
让我们实现一个完整的JWT认证中间件,包含以下功能:
go复制func JWTAuthMiddleware(secret string) gin.HandlerFunc {
return func(c *gin.Context) {
// 1. 从多种位置获取token
tokenString := extractToken(c)
if tokenString == "" {
respondUnauthorized(c, "认证信息缺失")
return
}
// 2. 解析并验证token
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(secret), nil
})
if err != nil || !token.Valid {
respondUnauthorized(c, "认证信息无效")
return
}
// 3. 处理token刷新
if claims, ok := token.Claims.(jwt.MapClaims); ok {
if shouldRefresh(claims) {
newToken := refreshToken(claims, secret)
c.Header("X-New-Token", newToken)
}
// 4. 设置用户上下文
setUserContext(c, claims)
}
c.Next()
}
}
func extractToken(c *gin.Context) string {
// 从Authorization头获取
if token := c.GetHeader("Authorization"); token != "" {
return strings.TrimPrefix(token, "Bearer ")
}
// 从cookie获取
if token, err := c.Cookie("auth_token"); err == nil {
return token
}
// 从查询参数获取
return c.Query("token")
}
下面是一个完整的性能监控中间件,可以记录请求处理时间、内存消耗等指标,并集成Prometheus:
go复制func MetricsMiddleware() gin.HandlerFunc {
// 初始化Prometheus指标
requestDuration := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests",
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10},
},
[]string{"method", "path", "status"},
)
prometheus.MustRegister(requestDuration)
return func(c *gin.Context) {
start := time.Now()
memBefore := getMemoryUsage()
c.Next()
duration := time.Since(start).Seconds()
memAfter := getMemoryUsage()
// 记录指标
requestDuration.WithLabelValues(
c.Request.Method,
c.FullPath(),
strconv.Itoa(c.Writer.Status()),
).Observe(duration)
// 记录内存变化
c.Set("memory_usage", memAfter-memBefore)
}
}
func getMemoryUsage() uint64 {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return m.Alloc
}
在复杂应用中,中间件可能需要访问数据库连接、配置等资源。我们可以使用闭包实现依赖注入:
go复制func RateLimitMiddleware(store limiter.Store) gin.HandlerFunc {
limiter := limiter.NewLimiter(store, limiter.Rate{
Period: 1 * time.Minute,
Limit: 100,
})
return func(c *gin.Context) {
key := getClientKey(c)
if !limiter.Allow(key) {
c.AbortWithStatusJSON(429, gin.H{
"error": "请求过于频繁",
})
return
}
c.Next()
}
}
// 使用示例
func main() {
r := gin.Default()
redisStore := limiter.NewRedisStore(redisClient)
r.Use(RateLimitMiddleware(redisStore))
// ...
}
有时我们需要根据请求特征动态调整中间件行为。下面是一个支持条件跳过的中间件模式:
go复制func SkipableMiddleware(shouldSkip func(*gin.Context) bool, middleware gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
if shouldSkip(c) {
c.Next()
return
}
middleware(c)
}
}
// 使用示例:仅对API路由应用JSON解析中间件
func main() {
r := gin.Default()
jsonMiddleware := gin.CustomRecovery(func(c *gin.Context) {
var json map[string]interface{}
if err := c.ShouldBindJSON(&json); err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": "无效的JSON"})
return
}
c.Set("request_body", json)
c.Next()
})
r.Use(SkipableMiddleware(
func(c *gin.Context) bool {
return !strings.HasPrefix(c.Request.URL.Path, "/api")
},
jsonMiddleware,
))
}
go复制// 不好的做法:直接在中间件中查询数据库
func BadAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
user, err := db.GetUserByToken(c.GetHeader("Token")) // 同步阻塞
if err != nil {
c.AbortWithStatus(401)
return
}
c.Set("user", user)
c.Next()
}
}
// 改进方案:使用JWT等无状态认证或缓存用户数据
func BetterAuthMiddleware(cache *UserCache) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Token")
if user, ok := cache.Get(token); ok {
c.Set("user", user)
c.Next()
return
}
c.AbortWithStatus(401)
}
}
使用Go的testing包对中间件进行性能测试:
go复制func BenchmarkAuthMiddleware(b *testing.B) {
r := gin.New()
r.Use(AuthMiddleware())
r.GET("/test", func(c *gin.Context) {
c.String(200, "OK")
})
req, _ := http.NewRequest("GET", "/test", nil)
req.Header.Set("Authorization", "Bearer valid_token")
w := httptest.NewRecorder()
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.ServeHTTP(w, req)
}
}
优化建议:
中间件作为独立单元应该被充分测试。测试要点包括:
go复制func TestAuthMiddleware(t *testing.T) {
// 准备测试用例
tests := []struct {
name string
token string
wantCode int
}{
{"valid token", "good_token", 200},
{"empty token", "", 401},
{"invalid token", "bad_token", 403},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 创建测试路由
r := gin.New()
r.Use(AuthMiddleware())
r.GET("/test", func(c *gin.Context) {
c.String(200, "OK")
})
// 创建测试请求
req, _ := http.NewRequest("GET", "/test", nil)
if tt.token != "" {
req.Header.Set("Authorization", "Bearer "+tt.token)
}
// 记录响应
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
// 验证结果
if w.Code != tt.wantCode {
t.Errorf("expected status %d, got %d", tt.wantCode, w.Code)
}
})
}
}
测试多个中间件组合时的行为:
go复制func TestMiddlewareChain(t *testing.T) {
r := gin.New()
r.Use(
RequestIDMiddleware(),
LoggingMiddleware(),
AuthMiddleware(),
RateLimitMiddleware(),
)
r.GET("/test", func(c *gin.Context) {
c.String(200, "OK")
})
// 模拟连续请求测试限流
for i := 0; i < 110; i++ {
req, _ := http.NewRequest("GET", "/test", nil)
req.Header.Set("Authorization", "Bearer valid_token")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if i >= 100 && w.Code != 429 {
t.Errorf("expected rate limit after 100 requests, got %d on request %d", w.Code, i+1)
}
}
}
在实际部署中,中间件的顺序对系统行为有重大影响。推荐顺序:
go复制func setupRouter() *gin.Engine {
r := gin.New()
// 基础中间件
r.Use(
customRecovery(), // 最外层捕获panic
tracingMiddleware(), // 尽早建立trace
rateLimitMiddleware(), // 在认证前限流
)
// 业务中间件
api := r.Group("/api")
api.Use(
authMiddleware(), // 认证
loggingMiddleware(), // 认证后的详细日志
)
return r
}
将中间件配置外部化,支持动态调整:
yaml复制# config/middlewares.yaml
rate_limit:
enabled: true
requests_per_minute: 100
excluded_ips:
- 127.0.0.1
- 192.168.1.0/24
logging:
level: info
skip_paths:
- /healthcheck
- /metrics
加载配置并初始化中间件:
go复制func setupMiddlewares(r *gin.Engine, conf config.Config) {
if conf.RateLimit.Enabled {
limiter := NewRateLimiter(conf.RateLimit)
r.Use(limiter.Middleware())
}
logger := NewLogger(conf.Logging)
r.Use(logger.Middleware())
}
可能原因及解决方案:
Use()方法且路由定义在注册之后c.Abort()会中断后续中间件Group()时注意路径前缀匹配安全共享数据的正确方式:
go复制func RequestIDMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 生成唯一请求ID
requestID := uuid.New().String()
// 设置到上下文和响应头
c.Set("request_id", requestID)
c.Header("X-Request-ID", requestID)
c.Next()
}
}
func LoggingMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 从上下文获取请求ID
requestID, _ := c.Get("request_id")
log.Printf("[%s] %s %s", requestID, c.Request.Method, c.Request.URL)
c.Next()
}
}
中间件中常见的内存泄漏场景:
解决方案:
go复制func SafeMiddleware(resourcePool *ResourcePool) gin.HandlerFunc {
return func(c *gin.Context) {
// 从池中获取资源
resource := resourcePool.Get()
defer resourcePool.Put(resource) // 确保归还
// 使用资源处理请求
c.Next()
}
}
实现类似Unix管道的中间件组合方式:
go复制type MiddlewarePipe func(gin.HandlerFunc) gin.HandlerFunc
func Compose(middlewares ...MiddlewarePipe) MiddlewarePipe {
return func(final gin.HandlerFunc) gin.HandlerFunc {
handler := final
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
}
// 使用示例
func main() {
r := gin.Default()
pipe := Compose(
addRequestID,
logRequest,
authRequired,
)
r.GET("/protected", pipe(func(c *gin.Context) {
c.String(200, "敏感数据")
}))
}
根据请求特征动态选择中间件分支:
go复制func ConditionalMiddleware(
condition func(*gin.Context) bool,
trueMiddleware, falseMiddleware gin.HandlerFunc,
) gin.HandlerFunc {
return func(c *gin.Context) {
if condition(c) {
trueMiddleware(c)
} else {
falseMiddleware(c)
}
}
}
// 使用示例:对移动端和桌面端应用不同中间件
func main() {
r := gin.Default()
r.Use(ConditionalMiddleware(
func(c *gin.Context) bool {
return strings.Contains(c.GetHeader("User-Agent"), "Mobile")
},
mobileMiddleware(),
desktopMiddleware(),
))
}
go复制func TracingMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tracer := otel.Tracer("gin")
ctx, span := tracer.Start(c.Request.Context(), c.FullPath())
defer span.End()
// 记录请求属性
span.SetAttributes(
attribute.String("http.method", c.Request.Method),
attribute.String("http.path", c.Request.URL.Path),
)
// 传播trace上下文
c.Request = c.Request.WithContext(ctx)
c.Next()
// 记录响应状态
span.SetStatus(codes.Ok, "")
span.SetAttributes(
attribute.Int("http.status", c.Writer.Status()),
)
}
}
go复制func PrometheusMiddleware() gin.HandlerFunc {
// 定义指标
requestsTotal := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "path", "status"},
)
requestDuration := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests",
Buckets: prometheus.DefBuckets,
},
[]string{"method", "path"},
)
prometheus.MustRegister(requestsTotal, requestDuration)
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start).Seconds()
status := strconv.Itoa(c.Writer.Status())
requestsTotal.WithLabelValues(
c.Request.Method,
c.FullPath(),
status,
).Inc()
requestDuration.WithLabelValues(
c.Request.Method,
c.FullPath(),
).Observe(duration)
}
}
减少GC压力是关键优化方向。以下技术可以实现零内存分配:
go复制var ctxPool = sync.Pool{
New: func() interface{} {
return &CustomContext{
buf: make([]byte, 0, 1024),
}
},
}
func ZeroAllocMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
customCtx := ctxPool.Get().(*CustomContext)
defer ctxPool.Put(customCtx)
customCtx.Reset() // 重置状态
c.Set("custom_ctx", customCtx)
c.Next()
}
}
go复制func JsonParseMiddleware() gin.HandlerFunc {
var bufPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, 4096))
},
}
return func(c *gin.Context) {
if c.ContentType() != "application/json" {
c.Next()
return
}
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
defer bufPool.Put(buf)
if _, err := buf.ReadFrom(c.Request.Body); err != nil {
c.AbortWithStatus(400)
return
}
var json map[string]interface{}
if err := jsoniter.ConfigFastest.Unmarshal(buf.Bytes(), &json); err != nil {
c.AbortWithStatus(400)
return
}
c.Set("json_body", json)
c.Next()
}
}
对于独立的中间件逻辑,可以使用并行处理提高性能:
go复制func ParallelMiddleware(middlewares ...gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
var wg sync.WaitGroup
wg.Add(len(middlewares))
for _, m := range middlewares {
go func(m gin.HandlerFunc) {
defer wg.Done()
// 创建子上下文避免并发问题
subCtx := c.Copy()
m(subCtx)
// 合并关键数据
if subCtx.IsAborted() {
c.Abort()
}
}(m)
}
wg.Wait()
c.Next()
}
}
// 使用示例
func main() {
r := gin.Default()
r.Use(ParallelMiddleware(
logRequest,
checkAuth,
validateInput,
))
// ...
}
注意:并行中间件仅适用于无状态且不修改共享数据的中间件,使用时需要谨慎评估线程安全性。
自动设置安全相关的HTTP头部:
go复制func SecurityHeadersMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 基础安全头部
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-Frame-Options", "DENY")
c.Header("X-XSS-Protection", "1; mode=block")
// CSP头部
c.Header("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com")
// 特征策略
c.Header("Feature-Policy",
"geolocation 'none'; microphone 'none'; camera 'none'")
c.Next()
}
}
防御常见Web攻击:
go复制func RequestValidationMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 检查内容类型
if c.Request.Method == "POST" &&
!strings.Contains(c.GetHeader("Content-Type"), "application/json") {
c.AbortWithStatus(415)
return
}
// 验证请求大小
if c.Request.ContentLength > 10<<20 { // 10MB限制
c.AbortWithStatus(413)
return
}
// 清理路径防止目录遍历
if strings.Contains(c.Request.URL.Path, "../") {
c.AbortWithStatus(400)
return
}
c.Next()
}
}
开发专用调试中间件:
go复制func DebugMiddleware(enable bool) gin.HandlerFunc {
return func(c *gin.Context) {
if !enable {
c.Next()
return
}
// 记录请求开始时间
start := time.Now()
// 打印请求信息
fmt.Printf("--> %s %s\n", c.Request.Method, c.Request.URL)
fmt.Println("Headers:")
for k, v := range c.Request.Header {
fmt.Printf(" %s: %v\n", k, v)
}
// 捕获响应
writer := &responseWriter{ResponseWriter: c.Writer}
c.Writer = writer
defer func() {
// 打印响应信息
fmt.Printf("<-- %d %s (%.2fms)\n",
writer.status,
http.StatusText(writer.status),
time.Since(start).Seconds()*1000)
fmt.Println("Response Headers:")
for k, v := range writer.Header() {
fmt.Printf(" %s: %v\n", k, v)
}
if writer.body.Len() > 0 {
fmt.Println("Response Body Preview:")
if writer.body.Len() > 200 {
fmt.Println(string(writer.body.Bytes()[:200]), "...")
} else {
fmt.Println(writer.body.String())
}
}
}()
c.Next()
}
}
type responseWriter struct {
gin.ResponseWriter
body bytes.Buffer
status int
}
func (w *responseWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w *responseWriter) WriteHeader(statusCode int) {
w.status = statusCode
w.ResponseWriter.WriteHeader(statusCode)
}
在调试复杂中间件链时,可以使用标记追踪执行流程:
go复制func TraceMiddleware(tag string) gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Printf("[%s] Enter %s\n", tag, time.Now().Format("15:04:05.000"))
defer func() {
fmt.Printf("[%s] Exit %s\n", tag, time.Now().Format("15:04:05.000"))
}()
c.Next()
}
}
// 使用示例
func main() {
r := gin.Default()
r.Use(
TraceMiddleware("auth"),
authMiddleware(),
TraceMiddleware("logger"),
loggingMiddleware(),
)
// ...
}
在微服务架构中,中间件可以统一处理服务间通信的公共逻辑:
go复制func ServiceClientMiddleware(client *http.Client) gin.HandlerFunc {
return func(c *gin.Context) {
// 注入统一的请求ID
reqID := c.GetString("X-Request-ID")
if reqID == "" {
reqID = uuid.New().String()
c.Set("X-Request-ID", reqID)
}
// 设置超时控制
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
// 创建带超时的HTTP客户端
c.Set("service_client", &ServiceClient{
Client: client,
Context: ctx,
Headers: map[string]string{
"X-Request-ID": reqID,
"X-Caller-Service": "web-api",
},
})
c.Next()
}
}
实现微服务调用的断路器模式:
go复制func CircuitBreakerMiddleware(name string, threshold int) gin.HandlerFunc {
cb := &circuitBreaker{
name: name,
threshold: threshold,
state: stateClosed,
}
return func(c *gin.Context) {
if cb.state == stateOpen {
c.AbortWithStatus(503)
return
}
start := time.Now()
defer func() {
if c.Writer.Status() >= 500 {
cb.recordFailure()
} else {
cb.recordSuccess()
}
}()
c.Next()
}
}
type circuitBreaker struct {
name string
threshold int
failures int
state int
}
const (
stateClosed = iota
stateOpen
stateHalfOpen
)
func (cb *circuitBreaker) recordFailure() {
cb.failures++
if cb.failures >= cb.threshold {
cb.state = stateOpen
time.AfterFunc(30*time.Second, func() {
cb.state = stateHalfOpen
cb.failures = cb.threshold / 2
})
}
}
func (cb *circuitBreaker) recordSuccess() {
if cb.state == stateHalfOpen {
cb.failures--
if cb.failures <= 0 {
cb.state = stateClosed
cb.failures = 0
}
}
}
优雅处理API版本迁移:
go复制func VersioningMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 从Header、Query或Path获取版本信息
version := c.GetHeader("X-API-Version")
if version == "" {
version = c.Query("v")
}
// 设置默认版本
if version == "" {
version = "v1"
}
// 验证版本有效性
if !isValidVersion(version) {
c.AbortWithStatusJSON(400, gin.H{
"error": "unsupported API version",
"supported_versions": []string{"v1", "v2"},
})
return
}
// 设置版本上下文
c.Set("api_version", version)
// 根据版本重写请求路径
if strings.HasPrefix(c.Request.URL.Path, "/api/") {
c.Request.URL.Path = fmt.Sprintf("/api/%s%s",
version,
strings.TrimPrefix(c.Request.URL.Path, "/api"))
}
c.Next()
}
}
处理旧版本客户端的兼容逻辑:
go复制func BackwardCompatibleMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
// 只处理成功响应
if c.Writer.Status() != 200 {
return
}
version := c.GetString("api_version")
if version == "v1" {
// 转换v2响应为v1格式
var v2Response map[string]interface{}
if err := json.Unmarshal(c.Writer.(*responseWriter).body.Bytes(), &v2Response); err == nil {
// 执行格式转换
v1Response := convertV2ToV1(v2Response)
c.JSON(200, v1Response)
}
}
}
}
使用pprof分析中间件性能瓶颈:
go复制func ProfilerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.URL.Path == "/debug/pprof" {
pprof.Index(c.Writer, c.Request)
c.Abort()
return
}
if strings.HasPrefix(c.Request.URL.Path, "/debug/pprof/") {
switch strings.TrimPrefix(c.Request.URL.Path, "/debug/pprof/") {
case "cmdline":
pprof.Cmdline(c.Writer, c.Request)
case "profile":
pprof.Profile(c.Writer, c.Request)
case "symbol":
pprof.Symbol(c.Writer, c.Request)
case "trace":
pprof.Trace(c.Writer, c.Request)
default:
pprof.Index(c.Writer, c.Request)
}
c.Abort()
return
}
c.Next()
}
}
实现中间件的动态更新:
go复制type ReloadableMiddleware struct {
mu sync.RWMutex
current gin.HandlerFunc
loader func() (gin.HandlerFunc, error)
lastLoad time.Time
loadEvery time.Duration
}
func NewReloadableMiddleware(
loader func() (gin.HandlerFunc, error),
loadEvery time.Duration,
) *ReloadableMiddleware {
m := &ReloadableMiddleware{
loader: loader,
loadEvery: loadEvery,
}
m.reload()
return m
}
func (m *ReloadableMiddleware) reload() error {
newHandler, err := m.loader()
if err != nil {
return err
}
m.mu.Lock()
defer m.mu.Unlock()
m.current = newHandler
m.lastLoad = time.Now()
return nil
}
func (m *ReloadableMiddleware) Handler() gin.HandlerFunc {
return func(c *gin.Context) {
// 检查是否需要重新加载
if time.Since(m.lastLoad) > m.loadEvery {
go m.reload() // 异步重载
}
m.mu.RLock()
handler := m.current
m.mu.RUnlock()
handler(c)
}
}
// 使用示例
func main() {
r := gin.Default()
configLoader := func() (gin.HandlerFunc, error) {
// 从外部加载配置并创建中间件
config, err := loadConfig()
if err != nil {
return nil, err
}
return RateLimitMiddleware(config), nil
}
r.Use(NewReloadableMiddleware(configLoader, 1*time.Minute).Handler())
r.Run(":8080")
}
go复制func ErrorHandlerMiddleware() gin.HandlerFunc {