全局中间件 #
一、全局中间件概述 #
1.1 什么是全局中间件 #
全局中间件应用于所有路由请求,在请求到达任何处理函数之前都会执行:
go
func main() {
r := gin.New()
// 全局中间件 - 应用于所有路由
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Run()
}
1.2 全局中间件特点 #
| 特点 | 说明 |
|---|---|
| 全局生效 | 应用于所有路由 |
| 优先执行 | 在路由中间件之前执行 |
| 统一处理 | 适合日志、恢复等通用功能 |
二、内置全局中间件 #
2.1 Logger中间件 #
go
func main() {
r := gin.New()
// Logger中间件记录请求日志
r.Use(gin.Logger())
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello")
})
r.Run()
}
// 日志输出示例:
// [GIN] 2024/01/01 - 10:00:00 | 200 | 12.345µs | 127.0.0.1 | GET "/"
2.2 自定义Logger格式 #
go
func main() {
r := gin.New()
// 自定义Logger格式
r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("[GIN] %s | %s | %d | %v | %s | %s %s\n",
param.TimeStamp.Format("2006/01/02 - 15:04:05"),
param.ClientIP,
param.StatusCode,
param.Latency,
param.Method,
param.Path,
param.ErrorMessage,
)
}))
r.Run()
}
2.3 LoggerWithWriter #
go
func main() {
r := gin.New()
// 将日志写入文件
file, _ := os.Create("gin.log")
r.Use(gin.LoggerWithWriter(file))
r.Run()
}
2.4 Recovery中间件 #
go
func main() {
r := gin.New()
// Recovery中间件捕获panic
r.Use(gin.Recovery())
r.GET("/panic", func(c *gin.Context) {
panic("Something went wrong!")
})
r.Run()
}
// panic被捕获,返回500错误,服务不会崩溃
2.5 自定义Recovery #
go
func main() {
r := gin.New()
// 自定义Recovery处理
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
if err, ok := recovered.(string); ok {
c.JSON(500, gin.H{
"code": 500,
"message": "服务器内部错误",
"error": err,
})
}
c.AbortWithStatus(500)
}))
r.GET("/panic", func(c *gin.Context) {
panic("Something went wrong!")
})
r.Run()
}
2.6 Default方法 #
go
func main() {
// gin.Default() 默认包含Logger和Recovery
r := gin.Default()
// 等价于
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Run()
}
三、自定义全局中间件 #
3.1 基本结构 #
go
func MyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 前置处理
c.Next()
// 后置处理
}
}
func main() {
r := gin.New()
r.Use(MyMiddleware())
r.Run()
}
3.2 请求日志中间件 #
go
func RequestLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// 请求信息
log.Printf("[REQUEST] %s %s %s",
c.Request.Method,
c.Request.URL.Path,
c.ClientIP(),
)
c.Next()
// 响应信息
log.Printf("[RESPONSE] %s %s %d %v",
c.Request.Method,
c.Request.URL.Path,
c.Writer.Status(),
time.Since(start),
)
}
}
func main() {
r := gin.New()
r.Use(RequestLogger())
r.Run()
}
3.3 请求ID中间件 #
go
func RequestID() gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求头获取或生成请求ID
requestID := c.GetHeader("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
// 设置到上下文和响应头
c.Set("requestId", requestID)
c.Header("X-Request-ID", requestID)
c.Next()
}
}
func main() {
r := gin.New()
r.Use(RequestID())
r.Run()
}
3.4 CORS中间件 #
go
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Header("Access-Control-Expose-Headers", "Content-Length")
c.Header("Access-Control-Max-Age", "86400")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func main() {
r := gin.New()
r.Use(CORS())
r.Run()
}
3.5 安全头中间件 #
go
func SecurityHeaders() 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")
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
c.Header("Content-Security-Policy", "default-src 'self'")
c.Next()
}
}
func main() {
r := gin.New()
r.Use(SecurityHeaders())
r.Run()
}
3.6 性能监控中间件 #
go
func PerformanceMonitor() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
latency := time.Since(start)
// 慢请求告警
if latency > time.Second {
log.Printf("[SLOW] %s %s took %v",
c.Request.Method,
c.Request.URL.Path,
latency,
)
}
// 记录性能指标
metrics.RecordLatency(latency)
metrics.RecordStatus(c.Writer.Status())
}
}
func main() {
r := gin.New()
r.Use(PerformanceMonitor())
r.Run()
}
3.7 限流中间件 #
go
func RateLimit(rps int) gin.HandlerFunc {
limiter := rate.NewLimiter(rate.Limit(rps), rps*2)
return func(c *gin.Context) {
if !limiter.Allow() {
c.AbortWithStatusJSON(429, gin.H{
"code": 429,
"message": "请求过于频繁",
})
return
}
c.Next()
}
}
func main() {
r := gin.New()
r.Use(RateLimit(100)) // 每秒100个请求
r.Run()
}
四、中间件配置 #
4.1 配置化中间件 #
go
type MiddlewareConfig struct {
EnableLogger bool
EnableRecovery bool
EnableCORS bool
EnableRateLimit bool
RateLimitRPS int
CORSOrigins []string
}
func SetupMiddleware(r *gin.Engine, config *MiddlewareConfig) {
if config.EnableLogger {
r.Use(gin.Logger())
}
if config.EnableRecovery {
r.Use(gin.Recovery())
}
if config.EnableCORS {
r.Use(CORSWithConfig(config.CORSOrigins))
}
if config.EnableRateLimit {
r.Use(RateLimit(config.RateLimitRPS))
}
}
func main() {
config := &MiddlewareConfig{
EnableLogger: true,
EnableRecovery: true,
EnableCORS: true,
EnableRateLimit: true,
RateLimitRPS: 100,
CORSOrigins: []string{"*"},
}
r := gin.New()
SetupMiddleware(r, config)
r.Run()
}
4.2 环境区分 #
go
func main() {
r := gin.New()
// 根据环境配置中间件
switch gin.Mode() {
case gin.DebugMode:
r.Use(gin.Logger())
r.Use(DebugMiddleware())
case gin.ReleaseMode:
r.Use(gin.Recovery())
r.Use(ProductionMiddleware())
r.Use(RateLimit(100))
case gin.TestMode:
r.Use(TestMiddleware())
}
r.Run()
}
五、中间件链 #
5.1 链式注册 #
go
func main() {
r := gin.New()
// 链式注册多个中间件
r.Use(
RequestID(),
RequestLogger(),
gin.Recovery(),
CORS(),
SecurityHeaders(),
)
r.Run()
}
5.2 中间件顺序 #
go
func main() {
r := gin.New()
// 推荐的中间件顺序
r.Use(
// 1. 请求ID(最先执行,方便追踪)
RequestID(),
// 2. 日志记录
RequestLogger(),
// 3. 错误恢复(捕获后续panic)
gin.Recovery(),
// 4. 安全相关
SecurityHeaders(),
CORS(),
// 5. 限流(保护服务器)
RateLimit(100),
)
r.Run()
}
六、全局中间件最佳实践 #
6.1 错误处理 #
go
func RecoveryMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// 记录错误
log.Printf("[PANIC] %v\n%s", err, debug.Stack())
// 返回错误响应
c.AbortWithStatusJSON(500, gin.H{
"code": 500,
"message": "服务器内部错误",
})
}
}()
c.Next()
}
}
6.2 优雅关闭 #
go
func main() {
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
srv := &http.Server{
Addr: ":8080",
Handler: r,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 优雅关闭
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exiting")
}
6.3 健康检查 #
go
func main() {
r := gin.New()
r.Use(gin.Recovery())
// 健康检查不需要日志
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
// 其他路由使用日志
api := r.Group("/api")
api.Use(gin.Logger())
{
api.GET("/users", listUsers)
}
r.Run()
}
七、总结 #
7.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 注册方式 | r.Use(middleware) |
| 内置中间件 | Logger、Recovery |
| 执行顺序 | 注册顺序即执行顺序 |
| 应用场景 | 日志、恢复、安全、限流 |
7.2 最佳实践 #
| 实践 | 说明 |
|---|---|
| 合理顺序 | 请求ID → 日志 → 恢复 → 安全 → 限流 |
| 配置化 | 通过配置控制中间件开关 |
| 环境区分 | 不同环境使用不同中间件 |
| 健康检查 | 排除不必要的中间件 |
7.3 下一步 #
现在你已经掌握了全局中间件,接下来让我们学习 路由级中间件,深入了解路由级别的中间件应用!
最后更新:2026-03-28