Gin从入门到精通 (六)中间件

devtools/2025/2/28 19:46:23/

六 、中间件

Gin 是一个高性能的 Go Web 框架,其核心特性之一就是强大的中间件(Middleware)机制中间件允许开发者在 HTTP 请求处理流程的不同阶段插入自定义逻辑,例如日志记录、身份验证、请求限流等。

1.基本概念

gin中,中间件是一个函数,接受 gin.Context 作为参数,用于在请求到达路由处理函数之前或之后执行某些操作。

中间件可以

  • 修改请求或响应的数据
  • 中断请求处理流程(如权限校验失败时终止请求)
  • 传递数据给后续中间件或路由处理函数

场景应用场景

  • 日志记录 :记录请求的路径、方法、状态码、耗时等。
  • 身份认证:校验 JWT、Session 或 API Key。
  • 限流:限制单位时间内的请求数量。
  • 跨域处理(CORS):设置响应头允许跨域请求。
  • 缓存控制:为特定路由添加缓存头。

2.内置中间件

Gin 提供了一些常用中间件

  • gin.Logger():记录请求日志
  • gin.Recovery():捕获 panic 并恢复服务
  • gin.BasicAuth():HTTP 基础认证

当使用 gin.Default() 时,会自动启用日志和恢复中间件

r := gin.Default() // 自动启用日志和恢复中间件

实现代码如下:

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default(opts ...OptionFunc) *Engine {debugPrintWARNINGDefault()engine := New()engine.Use(Logger(), Recovery()) //默认启动日志和恢复中间件return engine.With(opts...)
}

如果不想使用默认中间件可以使用:

r := gin.New()

3.自定义中间件

你也可以创建自定义的中间件来执行一些操作,例如记录请求的开始时间和结束时间:

package mainimport ("github.com/gin-gonic/gin""log""time"
)func MyMiddleware(c *gin.Context) {// 在请求处理前执行逻辑(如记录日志、校验权限)start := time.Now()//Set是在Context上下文中设置键值对c.Set("address", "Nanjin")//--------请求前--------// 将控制权交给下一个中间件或路由处理函数c.Next()//--------请求后--------// 请求处理后执行逻辑(如记录耗时)latency := time.Since(start)log.Printf("Request took %v\n", latency)}func main() {r := gin.Default()r.Use(MyMiddleware)r.GET("/test", func(c *gin.Context) {//GetString是在Context上下文中获取键对应的值并且设置为字符串类型example := c.GetString("address")c.String(200, example)})r.Run(":8080")
}

此代码MyMiddleware中间件会在请求前设置 address的值为Nanjin , 并在请求后输出程序执行时间

4.注册中间件

  r:= gin.Default()r.Use(MyMiddleware)
  • 路由组中间件
    仅对特定路由组生效:
  admin := router.Group("/admin")admin.Use(AuthMiddleware)
  • 单个路由中间件
    直接在路由定义中添加:
  router.GET("/user", MyMiddleware, func(c *gin.Context) {c.String(200, "User Info")})
r.Use(m1,m2,m3)

或者

  router.GET("/user", M1, func(c *gin.Context) {c.String(200, "User Info")},M2)

5.中间件的常用方法

在 Gin 框架中,中间件的常用方法主要围绕 gin.Context 对象和中间件的流程控制展开。以下是中间件开发中高频使用的方法及其详细说明和示例:

5.1 Next()

作用:将控制权交给后续中间件或路由处理函数,之后会继续执行当前中间件的剩余逻辑。
应用场景:在中间件中需要区分请求处理前和请求处理后的逻辑。

  • 示例
  func LogTimeMiddleware(c *gin.Context) {start := time.Now()c.Next() // 触发后续中间件和路由处理函数latency := time.Since(start)fmt.Printf("请求耗时: %v\n", latency)}

5.2 Abort()

作用:立即终止当前请求的后续处理流程,直接返回响应,不再执行后续中间件或路由处理函数。
应用场景:限校验失败、参数校验错误等需要中断请求的场景

示例

  func AuthMiddleware(c *gin.Context) {token := c.GetHeader("Authorization")if token == "" {c.AbortWithStatusJSON(401, gin.H{"error": "未授权"}) // 终止并返回错误c.Abort()  //c.Abort()拦截,后续的HandlerFunc就不会执行了}c.Next()}

5.3 Set()

作用:在上下文中存储数据,供后续中间件或路由处理函数使用。

示例

  func UserMiddleware(c *gin.Context) {user, _ := GetCurrentUser(c.Request)c.Set("user", user) // 存储用户信息c.Next()}// 在路由处理函数中获取func ProfileHandler(c *gin.Context) {user, _ := c.Get("user")c.JSON(200, user)}

5.4 Get()

作用:从上下文中获取存储的数据,需判断 exists 确认是否存在。
示例

  if val, ok := c.Get("user"); ok {user := val.(User) // 类型断言// 使用 user 对象}

5.5 AbortWithStatus()/ AbortWithStatusJSON()

作用:终止请求并返回指定 HTTP 状态码(可选返回 JSON 数据)。
示例

  if !user.IsAdmin {c.AbortWithStatusJSON(403, gin.H{"error": "权限不足"})return}

6.中间件的执行流程

Gin 中间件的执行遵循“洋葱模型”:

假设拿一根针插入洋葱,针尖会依次从一侧的最外层插入最里层,然后再从最里层依次插入另一侧的最外层,这个针尖就代表着中间件的执行顺序,一层一层的洋葱皮代表中间件,洋葱的中心代表路由处理函数
在这里插入图片描述

可以抽象为:

        ┌───────────────────────────┐│        中间件1(进入)       │└─────────────┬─────────────┘↓┌───────────────────────────┐│        中间件2(进入)       │└─────────────┬─────────────┘↓┌───────────────────────────┐│        路由处理函数         │└─────────────┬─────────────┘↓┌───────────────────────────┐│        中间件2(退出)       │└─────────────┬─────────────┘↓┌───────────────────────────┐│        中间件1(退出)       │└───────────────────────────┘

请求会依次经过中间件的“进入”逻辑,到达路由处理函数,然后逆序执行中间件的“退出”逻辑。

通过以下代码演示:

package mainimport ("fmt""github.com/gin-gonic/gin"
)func Middleware1(c *gin.Context) {fmt.Println("Middleware1 - Before")c.Next()fmt.Println("Middleware1 - After")
}func Middleware2(c *gin.Context) {fmt.Println("Middleware2 - Before")c.Next()fmt.Println("Middleware2 - After")
}func main() {r := gin.Default()r.Use(Middleware1, Middleware2)r.GET("/", func(c *gin.Context) {fmt.Println("main")c.String(200, "OK")})r.Run()
}

输出结果:

Middleware1 - Before
Middleware2 - Before
main
Middleware2 - After
Middleware1 - After

http://www.ppmy.cn/devtools/163428.html

相关文章

深入探究 C 语言内存函数:memcpy、memmove、memset 和 memcmp

一,常见的内存函数 在 C 语言的编程世界里,对内存的高效操作至关重要。C 标准库为我们提供了一系列强大的内存操作函数,其中 memcpy、memmove、memset 和 memcmp 这四个函数是处理内存数据的得力助手。接下来,让我们深入了解它们…

stm32四种方式精密控制步进电机

在搭建完clion的开发环境后,我决定重写之前的项目并优化完善,争取做出完全可落地的东西,也结合要写的论文内容一同学习下去。 因此,首当其冲的就是回到步进电机控制领域,把之前使用中断溢出进行步进电机控制的方案进行…

深入理解 CSS pointer-events: none:穿透点击的魔法

一、什么是 pointer-events: none? pointer-events: none 是一个强大的 CSS 属性,它控制元素是否响应鼠标/触摸事件(如点击、悬停、拖拽)。当设置为 none 时,元素会变得“透明”,事件会直接穿透到下方的元…

linux vim 撤销 回退操作

在Linux的vim编辑器中,撤销和回退操作是非常基本的,但它们可以通过不同的方式实现,具体取决于你想要的精确效果。下面是一些常用的方法: 1. 撤销(Undo) 单个撤销: 你可以通过按下u键来撤销上一…

java项目之基于ssm的线上旅游体验系统(源码+文档)

项目简介 基于ssm的线上旅游体验系统实现了以下功能: 用户信息管理: 用户信息新增 用户信息修改 景点信息管理: 景点信息添加 景点信息删除 景点信息修改 景点类型管理: 景点类型添加 景点类型删除 景点类型修改 景点留言管理…

REACT学习第三幕--沉睡花园

什么是Hooks? 在react中,useState以及任何其他以use开头的函数都称为Hook(钩子),所以Hooks就是代表着use函数的集合,也就是钩子的集合 Hooks就是一堆功能函数,一个组件想要实现哪些功能就可以…

DeepSeek开源:FlashMLA深度解析:Hopper架构上的大模型推理革命

2025年2月24日,DeepSeek以「开源周」首日发布的FlashMLA技术,重新定义了Hopper架构GPU在AI推理领域的性能极限。这款专为NVIDIA H800/H100系列优化的MLA(Multi-head Latent Attention)解码内核,通过突破性算法设计与硬件协同优化,在可变长度序列处理场景中实现了3000GB/s…

sklearn中的决策树-分类树:剪枝参数

剪枝参数 在不加限制的情况下,一棵决策树会生长到衡量不纯度的指标最优,或者没有更多的特征可用为止。这样的决策树 往往会过拟合。为了让决策树有更好的泛化性,我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大,正确的剪枝策…