Gin中间件
2025/12/23大约 7 分钟
Gin 中间件是 Web 应用的核心机制,用于实现日志、认证、CORS、错误处理等跨切面需求。本文通过完整可运行的示例,并结合 Java Filter 对比,帮助你快速掌握中间件开发。
Gin 中间件详解:日志、异常捕获、CORS、认证
Java 对比:Gin 的中间件类似 Java 的
Filter或 Spring 的Interceptor,但更灵活易用。
一、项目初始化
1.1 创建项目
cd ~/GolandProjects
mkdir go-gin-middleware && cd go-gin-middleware
go mod init go-gin-middleware
# 安装 Gin(指定兼容版本)
go get github.com/gin-gonic/gin@v1.9.1
# 安装 CORS 中间件(可选)
go get github.com/gin-contrib/cors@v1.4.0版本说明:
- 所有示例代码兼容 Go 1.18-1.22 版本
- Gin v1.9.1 和 CORS v1.4.0 已在 Go 1.22.2 上测试通过
- 如需使用最新版本:
go get -u github.com/gin-gonic/gin(需 Go 1.23+)
二、中间件基础
2.1 编写简单中间件
创建 middleware_basic.go:
# 创建文件
nano middleware_basic.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
// 最小化中间件
func MyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 请求前处理
fmt.Println("Before request")
// 继续下一个中间件/Handler
c.Next()
// 响应后处理
fmt.Println("After request")
}
}
func main() {
r := gin.Default()
// 使用中间件
r.Use(MyMiddleware())
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello"})
})
r.Run(":8080")
}运行 & 测试
go run middleware_basic.go
# 在另一个终端
curl http://localhost:8080/
# 控制台输出:
# Before request
# After requestJava 对比:
- Gin 中间件 ≈
Filter.doFilter(request, response, chain)c.Next()≈chain.doFilter(request, response)
三、中间件中止执行
3.1 Token 认证中间件
创建 middleware_abort.go:
nano middleware_abort.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"github.com/gin-gonic/gin"
)
func TokenAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 检查 token
if c.Query("token") == "" {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort() // 中止后续处理
return
}
c.Next()
}
}
func main() {
r := gin.Default()
// 使用认证中间件
r.Use(TokenAuthMiddleware())
r.GET("/protected", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Access granted"})
})
r.Run(":8080")
}运行 & 测试
go run middleware_abort.go
# 没有 token,被拦截
curl http://localhost:8080/protected
# {"error":"Unauthorized"}
# 带 token,通过
curl 'http://localhost:8080/protected?token=abc123'
# {"message":"Access granted"}四、日志中间件
4.1 自定义日志中间件
创建 middleware_logger.go:
nano middleware_logger.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 日志中间件
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 记录请求信息
startTime := time.Now()
method := c.Request.Method
url := c.Request.URL.Path
// 继续处理
c.Next()
// 记录响应信息
statusCode := c.Writer.Status()
duration := time.Since(startTime)
fmt.Printf("[%s] %s %d (%v)\n",
method, url, statusCode, duration)
}
}
func main() {
r := gin.New() // 不使用默认中间件
// 使用自定义日志中间件
r.Use(LoggerMiddleware())
r.Use(gin.Recovery())
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello"})
})
r.GET("/slow", func(c *gin.Context) {
time.Sleep(100 * time.Millisecond)
c.JSON(200, gin.H{"message": "Slow endpoint"})
})
r.Run(":8080")
}运行 & 测试
go run middleware_logger.go
# 访问接口
curl http://localhost:8080/
# 控制台输出:[GET] / 200 (123.456µs)
curl http://localhost:8080/slow
# 控制台输出:[GET] /slow 200 (100.234ms)Java 对比:
- Gin 日志中间件 ≈ Logback 的
Filter+MDCc.Next()前后处理 ≈ Filter 的doFilter前后
五、异常捕获中间件
5.1 自定义 Recovery 中间件
创建 middleware_recovery.go:
nano middleware_recovery.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
// 自定义 Recovery 中间件
func CustomRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// 记录错误
fmt.Printf("Panic: %v\n", err)
// 返回友好的错误响应
c.JSON(500, gin.H{
"error": "Internal Server Error",
"message": fmt.Sprintf("%v", err),
})
// 中止处理链
c.Abort()
}
}()
c.Next()
}
}
func main() {
r := gin.New()
// 使用自定义 Recovery
r.Use(CustomRecovery())
r.Use(gin.Logger())
// 正常路由
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "OK"})
})
// 故意触发 panic
r.GET("/panic", func(c *gin.Context) {
panic("Something went wrong!")
})
r.Run(":8080")
}运行 & 测试
go run middleware_recovery.go
# 正常请求
curl http://localhost:8080/
# {"message":"OK"}
# 触发 panic,被捕获
curl http://localhost:8080/panic
# {"error":"Internal Server Error","message":"Something went wrong!"}
# 控制台输出:Panic: Something went wrong!Java 对比:
- Custom Recovery ≈ Spring 的
@ControllerAdvice+@ExceptionHandlerdefer + recover()≈ Java 的try-catch
六、认证中间件
6.1 基础认证中间件
创建 middleware_auth.go:
nano middleware_auth.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"github.com/gin-gonic/gin"
)
// 基础认证中间件
func BasicAuth() gin.HandlerFunc {
return func(c *gin.Context) {
username, password, ok := c.Request.BasicAuth()
if !ok || username != "admin" || password != "password123" {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
// 认证成功,继续
c.Set("username", username) // 将用户信息存储在 context 中
c.Next()
}
}
func main() {
r := gin.Default()
// 公开路由
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Public endpoint"})
})
// 需要认证的路由
r.GET("/protected", BasicAuth(), func(c *gin.Context) {
username := c.GetString("username")
c.JSON(200, gin.H{"message": "Hello " + username})
})
r.Run(":8080")
}运行 & 测试
go run middleware_auth.go
# 无认证信息
curl http://localhost:8080/protected
# {"error":"Unauthorized"}
# 使用基础认证
curl -u admin:password123 http://localhost:8080/protected
# {"message":"Hello admin"}6.2 Token 认证(模拟 JWT)
创建 middleware_token.go:
nano middleware_token.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"strings"
"github.com/gin-gonic/gin"
)
// Token 认证中间件
func TokenAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求头获取 token
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(401, gin.H{"error": "Missing Authorization header"})
c.Abort()
return
}
// 验证 token 格式(Bearer xxx)
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
c.JSON(401, gin.H{"error": "Invalid Authorization header"})
c.Abort()
return
}
token := parts[1]
// 验证 token 有效性(简化版,实际应使用 JWT)
if !isValidToken(token) {
c.JSON(401, gin.H{"error": "Invalid token"})
c.Abort()
return
}
// 提取用户信息(从 token)
userID := extractUserID(token)
c.Set("user_id", userID)
c.Next()
}
}
// 验证 token(示例)
func isValidToken(token string) bool {
return token == "valid_token_123"
}
// 提取用户ID(示例)
func extractUserID(token string) string {
return "user123"
}
func main() {
r := gin.Default()
// 需要 token 的路由
r.GET("/profile", TokenAuth(), func(c *gin.Context) {
userID := c.GetString("user_id")
c.JSON(200, gin.H{
"user_id": userID,
"profile": "User profile data",
})
})
r.Run(":8080")
}运行 & 测试
go run middleware_token.go
# 无 token - 失败
curl http://localhost:8080/profile
# {"error":"Missing Authorization header"}
# 有效 token - 成功
curl -H "Authorization: Bearer valid_token_123" http://localhost:8080/profile
# {"user_id":"user123","profile":"User profile data"}Java 对比:
- Token Auth ≈ Spring Security 的
OncePerRequestFilter+ JWT- Bearer Token ≈ Spring Security 的
BearerTokenAuthenticationFilter
七、CORS 中间件
7.1 使用 gin-contrib/cors
创建 middleware_cors.go:
nano middleware_cors.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"time"
"github.com/gin-gonic/gin"
"github.com/gin-contrib/cors"
)
func main() {
r := gin.Default()
// 配置 CORS
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
r.GET("/api/data", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "CORS enabled"})
})
r.Run(":8080")
}运行 & 测试
go run middleware_cors.go
# 测试 CORS
curl -H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS http://localhost:8080/api/data7.2 自定义 CORS 中间件
创建 middleware_cors_custom.go:
nano middleware_cors_custom.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"github.com/gin-gonic/gin"
)
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func main() {
r := gin.Default()
r.Use(CORSMiddleware())
r.GET("/api/data", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Custom CORS enabled"})
})
r.Run(":8080")
}运行 & 测试
go run middleware_cors_custom.go
curl http://localhost:8080/api/data
# {"message":"Custom CORS enabled"}Java 对比:
- CORS 中间件 ≈ Spring 的
CorsFilter或@CrossOriginAllowOrigins≈@CrossOrigin(origins = "...")
八、中间件组合使用
8.1 完整示例
创建 middleware_complete.go:
nano middleware_complete.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 日志中间件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
fmt.Printf("[%s] %s %d (%v)\n",
c.Request.Method, c.Request.URL.Path, c.Writer.Status(), duration)
}
}
// 认证中间件
func Auth() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token != "Bearer secret" {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
c.Next()
}
}
// Recovery 中间件
func Recovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.JSON(500, gin.H{"error": "Internal Server Error"})
c.Abort()
}
}()
c.Next()
}
}
func main() {
r := gin.New()
// 全局中间件
r.Use(Logger())
r.Use(Recovery())
// 公开路由
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Public"})
})
// 需要认证的路由组
auth := r.Group("/api")
auth.Use(Auth())
{
auth.GET("/users", func(c *gin.Context) {
c.JSON(200, gin.H{"users": []string{"Alice", "Bob"}})
})
auth.POST("/users", func(c *gin.Context) {
c.JSON(201, gin.H{"message": "User created"})
})
}
r.Run(":8080")
}运行 & 测试
go run middleware_complete.go
# 公开路由
curl http://localhost:8080/
# {"message":"Public"}
# 需要认证的路由 - 无 token
curl http://localhost:8080/api/users
# {"error":"Unauthorized"}
# 需要认证的路由 - 有 token
curl -H "Authorization: Bearer secret" http://localhost:8080/api/users
# {"users":["Alice","Bob"]}九、对比总结
| 概念 | Gin | Spring/Java | 说明 |
|---|---|---|---|
| 中间件 | gin.HandlerFunc | Filter / Interceptor | Gin 更轻量 |
| 执行链 | c.Next() | chain.doFilter() | 类似概念 |
| 异常处理 | defer + recover() | @ExceptionHandler | Gin 更灵活 |
| CORS | cors.Config | @CrossOrigin | 配置方式不同 |
| 认证 | 自定义中间件 | Spring Security | Gin 需手动实现 |
十、常见问题
Q: 中间件的执行顺序是什么?
A: 按照注册顺序执行。c.Next() 之前的代码按顺序执行,之后的代码逆序执行。
Q: 全局中间件和路由级中间件有什么区别?
A:
- 全局中间件:
r.Use(middleware)对所有路由生效 - 路由级中间件:
r.GET("/path", middleware, handler)仅对该路由生效 - 分组中间件:
group.Use(middleware)对该分组下的所有路由生效
Q: 如何在中间件之间传递数据?
A: 使用 c.Set(key, value) 和 c.Get(key) 或 c.GetString(key)
十一、下一步
✅ 已掌握 Gin 中间件开发
→ 下一章:GORM 数据库(ORM、CRUD、事务)
→ 再下一章:配置管理(Viper)
祝你编码愉快!🚀
