Skip to content

中间件

Gin 中,中间件(Middleware)是处理 HTTP 请求的函数,它们可以在请求到达具体处理函数之前或之后执行一些操作。中间件常用于日志记录、请求验证、身份认证、跨域处理等场景。

1. 中间件基本概念

中间件是一个函数,它接收一个 gin.Context 对象,执行某些操作后,可以通过 c.Next() 调用传递请求给下一个中间件或最终的处理函数。如果在中间件中调用 c.Abort(),请求会停止传递,不会继续执行后续的中间件或处理函数。

2. 自定义中间件

一个最简单的中间件定义方式如下:

go
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

// 自定义中间件,记录请求时间
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 请求开始时间
		start := time.Now()

		// 调用下一个中间件或处理函数
		c.Next()

		// 请求结束时间
		duration := time.Since(start)
		fmt.Printf("Request took %v\n", duration)
	}
}

func main() {
	r := gin.Default()

	// 注册自定义中间件
	r.Use(Logger())

	// 设置路由
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello, World!")
	})

	// 启动服务
	r.Run(":8080")
}

3. 中间件解析

  1. 中间件结构:中间件是一个返回 gin.HandlerFunc 的函数,gin.HandlerFunc 本质上是一个带有 *gin.Context 参数的函数。
  2. 执行顺序
    • 当请求到来时,中间件按注册顺序执行(先注册的先执行)。
    • c.Next():让请求继续传递给下一个中间件或处理函数。
    • c.Abort():中断请求处理流程,后续的中间件或处理函数将不再执行。

4. 内置中间件

Gin 提供了一些常用的内置中间件,例如:

4.1 日志中间件

Gin 自带的日志中间件 gin.Logger() 用于输出请求日志。

go
r := gin.Default() // 默认的 Gin 引擎已包含 Logger 和 Recovery 中间件

gin.Default() 会自动使用 gin.Logger()gin.Recovery() 中间件。

4.2 恢复中间件

gin.Recovery() 是一个用于捕获异常并恢复的中间件,它会确保发生 panic 时不会导致服务器崩溃。

go
r := gin.Default() // gin.Default() 已默认包含 gin.Recovery()

4.3 CORS 中间件

跨域资源共享(CORS)是一个常见的中间件需求,Gin 通过 gincontrib/cors 提供了 CORS 支持。

go
import "github.com/gin-contrib/cors"

// 配置 CORS 中间件
r.Use(cors.Default()) // 默认配置的 CORS 中间件

5. 链式中间件

你可以将多个中间件链式组合,在不同的路由上使用不同的中间件。

go
r.GET("/user", Logger(), AuthMiddleware(), func(c *gin.Context) {
	c.String(200, "User Info")
})

r.POST("/login", func(c *gin.Context) {
	c.String(200, "Login")
})

每个路由可以使用不同的中间件,也可以将某些中间件应用到所有路由。

6. 常见的中间件场景

6.1 认证与授权中间件

通常用于检查请求头中的 token 或 session 是否有效。

go
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 假设从请求头中获取 token
		token := c.GetHeader("Authorization")
		if token != "valid-token" {
			c.JSON(401, gin.H{"message": "Unauthorized"})
			c.Abort() // 中断请求
			return
		}
		c.Next() // 继续处理请求
	}
}

r := gin.Default()
r.GET("/profile", AuthMiddleware(), func(c *gin.Context) {
	c.JSON(200, gin.H{"message": "User Profile"})
})

6.2 请求日志中间件

记录每个请求的相关信息,包括请求方法、路径和时间。

go
func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		c.Next() // 调用后续的中间件或处理函数
		duration := time.Since(start)
		fmt.Printf("Request %s %s took %v\n", c.Request.Method, c.Request.URL, duration)
	}
}

r := gin.Default()
r.Use(Logger()) // 使用自定义的日志中间件

6.3 限流中间件

你可以使用中间件来实现简单的限流功能,限制单位时间内可以接受的请求数量。

go
import "golang.org/x/time/rate"

func RateLimiter() gin.HandlerFunc {
	limiter := rate.NewLimiter(1, 3) // 每秒允许 1 次请求,最多允许 3 次突发请求
	return func(c *gin.Context) {
		if !limiter.Allow() {
			c.JSON(429, gin.H{"message": "Too many requests"})
			c.Abort()
			return
		}
		c.Next()
	}
}

r := gin.Default()
r.Use(RateLimiter()) // 使用限流中间件

7. 中间件的生命周期

  • 请求到达时:从客户端发送请求到达服务器时,所有注册的中间件会依次执行。
  • 处理请求时:中间件执行时,可以修改请求的内容,如修改请求头、查询参数等。
  • 请求完成后:中间件会在处理请求后执行清理操作,如日志记录、统计等。

8. 优先级与控制

  • 执行顺序:中间件的执行顺序是按注册顺序的,从上到下,先注册的中间件先执行。
  • 中断请求:如果你在中间件中调用了 c.Abort(),请求将被中断,后续的中间件和处理函数不会被执行。

9. 路由级别的中间件

你可以将中间件应用于特定的路由或路由组,而不是全局应用。

9.1 仅对某个路由使用中间件

go
r.GET("/admin", AdminAuthMiddleware(), func(c *gin.Context) {
	c.JSON(200, gin.H{"message": "Welcome Admin"})
})

9.2 对路由组使用中间件

go
adminGroup := r.Group("/admin")
adminGroup.Use(AdminAuthMiddleware()) // 路由组中所有路由都使用此中间件
adminGroup.GET("/dashboard", func(c *gin.Context) {
	c.JSON(200, gin.H{"message": "Admin Dashboard"})
})

10. 总结

Gin 中的中间件机制非常强大,能够灵活地在请求的生命周期中插入自定义的逻辑。通过中间件,你可以:

  • 记录日志、验证请求、处理跨域请求等。
  • 灵活地组合中间件,按需应用到全局、特定路由或路由组。
  • 控制请求的执行流程,如中止请求、延迟处理等。

中间件的使用使得你的应用更加模块化和可维护,且能够灵活应对复杂的请求处理需求。