Gin 框架中间件详细介绍

embedded/2024/11/22 7:29:34/
  1. 基本中间件结构:
// 基本中间件函数签名
func MiddlewareName() gin.HandlerFunc {return func(c *gin.Context) {// 处理请求前的逻辑c.Next()  // 处理下一个中间件或处理函数// 处理请求后的逻辑}
}
  1. 常用中间件示例:
package middlewareimport ("github.com/gin-gonic/gin""log""time"
)// 日志中间件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathc.Next()latency := time.Since(start)statusCode := c.Writer.Status()log.Printf("Path: %s | Status: %d | Latency: %v", path, statusCode, latency)}
}// 认证中间件
func Auth() gin.HandlerFunc {return func(c *gin.Context) {token := c.GetHeader("Authorization")if token == "" {c.JSON(401, gin.H{"error": "Authorization token required"})c.Abort()return}// 验证 tokenif !validateToken(token) {c.JSON(401, gin.H{"error": "Invalid token"})c.Abort()return}c.Next()}
}// 错误处理中间件
func ErrorHandler() gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {c.JSON(500, gin.H{"error": "Internal Server Error",})}}()c.Next()}
}// CORS 中间件
func CORS() gin.HandlerFunc {return func(c *gin.Context) {c.Writer.Header().Set("Access-Control-Allow-Origin", "*")c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")if c.Request.Method == "OPTIONS" {c.AbortWithStatus(204)return}c.Next()}
}
  1. 中间件的使用方式:
package mainimport ("github.com/gin-gonic/gin""your-project/middleware"
)func main() {r := gin.Default()// 全局中间件r.Use(middleware.Logger())r.Use(middleware.ErrorHandler())// 路由组中间件authorized := r.Group("/api")authorized.Use(middleware.Auth()){authorized.GET("/users", GetUsers)authorized.POST("/users", CreateUser)}// 单个路由中间件r.GET("/public", middleware.CORS(), PublicHandler)r.Run(":8080")
}
  1. 带配置的中间件
// 限流中间件
func RateLimit(limit int, duration time.Duration) gin.HandlerFunc {limiter := rate.NewLimiter(rate.Every(duration), limit)return func(c *gin.Context) {if !limiter.Allow() {c.JSON(429, gin.H{"error": "Too many requests"})c.Abort()return}c.Next()}
}// 缓存中间件
func Cache(duration time.Duration) gin.HandlerFunc {cache := make(map[string]cacheEntry)type cacheEntry struct {data      interface{}timestamp time.Time}return func(c *gin.Context) {key := c.Request.URL.Path// 检查缓存if entry, exists := cache[key]; exists {if time.Since(entry.timestamp) < duration {c.JSON(200, entry.data)c.Abort()return}}c.Next()// 存储响应到缓存cache[key] = cacheEntry{data:      c.Keys["response"],timestamp: time.Now(),}}
}
  1. 自定义上下文:
// 自定义上下文
type CustomContext struct {*gin.ContextUser *models.User
}// 用户上下文中间件
func UserContext() gin.HandlerFunc {return func(c *gin.Context) {user := getCurrentUser(c)customContext := &CustomContext{Context: c,User:    user,}c.Set("custom_context", customContext)c.Next()}
}
  1. 链式中间件
// 请求链路追踪
func RequestTracing() gin.HandlerFunc {return func(c *gin.Context) {traceID := generateTraceID()c.Set("trace_id", traceID)c.Next()// 记录请求完成log.Printf("Request completed: %s", traceID)}
}// 性能监控中间件
func Performance() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()c.Next()duration := time.Since(start)if duration > time.Second {log.Printf("Slow request: %s took %v", c.Request.URL.Path, duration)}}
}
  1. 中间件通信:
// 设置和获取中间件数据
func DataMiddleware() gin.HandlerFunc {return func(c *gin.Context) {// 设置数据c.Set("key", "value")c.Next()// 获取数据value, exists := c.Get("key")if exists {// 使用数据}}
}// 响应修改中间件
func ResponseModifier() gin.HandlerFunc {return func(c *gin.Context) {c.Next()// 修改响应status := c.Writer.Status()if status >= 400 {c.JSON(status, gin.H{"error":   true,"message": c.Keys["error"],})}}
}
  1. 高级中间件示例:
// JWT 认证中间件
func JWTAuth() gin.HandlerFunc {return func(c *gin.Context) {token := c.GetHeader("Authorization")if token == "" {c.JSON(401, gin.H{"error": "No token provided"})c.Abort()return}claims, err := parseToken(token)if err != nil {c.JSON(401, gin.H{"error": "Invalid token"})c.Abort()return}c.Set("user_id", claims.UserID)c.Next()}
}// 请求验证中间件
func ValidateRequest(schema interface{}) gin.HandlerFunc {return func(c *gin.Context) {if err := c.ShouldBindJSON(schema); err != nil {c.JSON(400, gin.H{"error": err.Error()})c.Abort()return}c.Set("validated_data", schema)c.Next()}
}// 请求限制中间件
func RequestLimit(maxRequests int, duration time.Duration) gin.HandlerFunc {clients := make(map[string][]time.Time)mu := &sync.Mutex{}return func(c *gin.Context) {ip := c.ClientIP()now := time.Now()mu.Lock()defer mu.Unlock()// 清理过期的请求记录clients[ip] = filterOldRequests(clients[ip], now.Add(-duration))if len(clients[ip]) >= maxRequests {c.JSON(429, gin.H{"error": "Too many requests"})c.Abort()return}clients[ip] = append(clients[ip], now)c.Next()}
}
  1. 测试中间件
func TestAuthMiddleware(t *testing.T) {// 创建测试路由r := gin.New()r.Use(Auth())r.GET("/test", func(c *gin.Context) {c.JSON(200, gin.H{"message": "success"})})// 创建测试请求w := httptest.NewRecorder()req, _ := http.NewRequest("GET", "/test", nil)// 不带 token 的请求r.ServeHTTP(w, req)assert.Equal(t, 401, w.Code)// 带有效 token 的请求w = httptest.NewRecorder()req.Header.Set("Authorization", "valid-token")r.ServeHTTP(w, req)assert.Equal(t, 200, w.Code)
}
  1. 中间件最佳实践:
// 中间件工厂
type MiddlewareConfig struct {EnableLogging boolLogLevel     stringTimeout      time.Duration
}func NewMiddleware(config MiddlewareConfig) gin.HandlerFunc {return func(c *gin.Context) {if config.EnableLogging {// 启用日志}// 设置超时ctx, cancel := context.WithTimeout(c.Request.Context(), config.Timeout)defer cancel()c.Request = c.Request.WithContext(ctx)done := make(chan bool)go func() {c.Next()done <- true}()select {case <-done:returncase <-ctx.Done():c.JSON(504, gin.H{"error": "request timeout"})c.Abort()}}
}

使用这些中间件时,需要注意:

  • 中间件的执行顺序很重要
  • 合理使用 c.Next()c.Abort()
  • 避免在中间件中存储太多数据
  • 注意性能影响
  • 做好错误处理
  • 保持中间件的独立性和可复用性

http://www.ppmy.cn/embedded/139559.html

相关文章

【NodeJS】Node.js是什么?能做什么?

👉博主介绍: 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主 ⛪️ 个人社区:个人社区 💞 个人主页:个人主页 🙉 专栏地址: ✅ Java 中级 🙉八股文专题:剑指大厂,手撕 J…

i春秋-签到题

练习平台地址 竞赛中心 题目描述 题目内容 点击GUESS后会有辨识细菌的选择题 全部完成后会有弹窗提示 输入nickname后提示获得flag F12检查 元素中没有发现信息 检查后发现flag在控制台中 flag flag{663a5c95-3050-4c3a-bb6e-bc4f2fb6c32e} 注意事项 flag不一定要在元素中找&a…

解决jacoco agent遇到反射的问题

问题分析 在我们使用jacoco的agent的过程中&#xff0c;经常越到代码里使用了反射&#xff0c;而jacoco在插桩时会动态插入字段$jacocoData来记录探针信息&#xff0c;这样我们在使用反射遍历类的属性时会因为多了一个字段而导致业务代码出错&#xff0c;那么遇到这样的问题&a…

【C++】从C到C++

C和C一些语法区别 1.三目运算符&#xff1a;在C语言中返回的是一个常量&#xff0c;是不能被赋值的&#xff1b;而C中返回的是变量&#xff0c;可以被赋值 2.C中的函数必须要写返回值类型 3.在全局下&#xff0c;C不允许int a;和int a10;等这种重定义二义性操作 4.在C中不要…

-bash: ./kafka-topics.sh: No such file or directory--解决方案

使用./kafka-topics.sh脚本出现以下错误 其实就是没在该脚本所在的目录运行&#xff0c;使用docker安装kafka的话&#xff0c;该脚本一般放在Kafka安装目录中的/opt/bitnami/kafka/bin 先启动并进去一个Kafka容器&#xff1a; docker start kafka-0 docker exec -it kafka-0…

Makefile 之 join

join $(join <list1>,<list2> ) 名称&#xff1a;连接函数——join。 功能&#xff1a;把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多&#xff0c; 那么&#xff0c;<list1>中的多出…

“闲置经济”成新消费趋势,万物新生长期成长性如何?

2024年&#xff0c;“以旧换新”成为消费热词。以“史上最长”双11为分界点&#xff0c;以旧换新进入下半场&#xff0c;消费市场将怎么发展&#xff0c;依然备受关注。 值得关注的是&#xff0c;自以旧换新工作启动以来&#xff0c;新产品销售量上行的同时&#xff0c;也带来…

【MATLAB】续行符号对字符串失效

前言 之前对于遇到的问题一般都是在之前的一篇文章后面更新&#xff0c;比如这样的: (原文链接) 现在想想这样其实不够直观&#xff0c;也不方便查找&#xff0c;所以计划之后如果遇到问题就直接记录成一篇文章&#xff0c;反正在一个专栏也好找。 问题描述 一般来说&#xff…