中间件
在 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. 中间件解析
- 中间件结构:中间件是一个返回
gin.HandlerFunc的函数,gin.HandlerFunc本质上是一个带有*gin.Context参数的函数。 - 执行顺序:
- 当请求到来时,中间件按注册顺序执行(先注册的先执行)。
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 中的中间件机制非常强大,能够灵活地在请求的生命周期中插入自定义的逻辑。通过中间件,你可以:
- 记录日志、验证请求、处理跨域请求等。
- 灵活地组合中间件,按需应用到全局、特定路由或路由组。
- 控制请求的执行流程,如中止请求、延迟处理等。
中间件的使用使得你的应用更加模块化和可维护,且能够灵活应对复杂的请求处理需求。