Gin拦截器(中间件)&Token拦截器实现

介绍

拦截器也叫中间件

什么是Gin拦截器

首先,我们来了解一下什么是拦截器。在Gin框架中,拦截器可以理解为一组函数,用于在处理HTTP请求之前或之后执行一些公共的逻辑。比如,在每次请求到达服务器之前,你可能想要记录请求的信息,或者进行权限校验。这时候,你可以使用Gin的拦截器来实现。

Gin拦截器的使用方法

在Gin中,你可以通过调用Use()方法来注册一个全局的拦截器。请注意,拦截器的注册顺序很重要,因为它们会按照注册的顺序依次执行。示例如下:

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 在请求之前打印日志
        log.Println("Request URL:", c.Request.URL)
        
        // 继续执行后续的处理函数
        c.Next()
        
        // 在请求之后打印日志
        log.Println("Response Status:", c.Writer.Status())
    }
}

func main() {
    r := gin.Default()
    
    // 注册全局拦截器
    r.Use(Logger())
    
    // 设置路由和处理函数
    r.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello, Gin!"})
    })
    
    r.Run(":8080")
}

在上述示例代码中,我们定义了一个名为Logger的拦截器。在每次请求到达服务器之前,Logger会打印出请求的URL;而在请求处理完成后,Logger会打印出响应的状态码。要注意的是,我们通过gin.Default()方法创建了一个默认的Gin引擎,并通过调用其Use()方法注册了Logger作为全局的拦截器。

Gin拦截器的执行流程

Gin框架的拦截器是基于洋葱模型(Onion Model)的执行流程。这也是典型的Pipeline设计模式。在洋葱模型中,请求先进入一系列的前置拦截器,然后再回到每个拦截器,执行相应处理函数,最后再回到每个拦截器,执行后置处理函数。 具体而言,当一个请求到达服务器时,Gin首先会调用所有注册的前置拦截器的处理函数。然后,Gin会调用相应的路由处理函数,处理请求的业务逻辑。最后,Gin又会调用所有注册的后置拦截器的处理函数。这样,整个流程就形成了一个环。 需要注意的是,拦截器中的c.Next()函数是用于将请求继续传递给下一个处理函数的关键点。如果你忽略了这个调用,后续的处理函数将无法被执行,从而导致错误。

Gin拦截器的特别之处

相比于其他web框架,Gin的拦截器具有一些特别之处: 1. 高性能:Gin的拦截器是通过高效的函数调用实现的,因此它们对性能的影响非常小。 2. 异常处理:Gin提供了Abort()函数和Aborted()方法,用于在拦截器中检测和处理异常。你可以在拦截器中使用这两个函数来实现自定义的异常处理逻辑。 3. 分组拦截器:Gin支持将拦截器分组应用于指定的路由组。这样,你可以将不同类型的拦截器应用于不同的路由组,更好地控制业务逻辑的处理流程。

拦截器的注意事项

在使用拦截器时,请务必注意以下几点: 1. 拦截器的注册顺序非常重要,一定要按照业务逻辑的需要注册拦截器。 2. 拦截器可以读取和修改请求的上下文,但请注意不要过度依赖拦截器中的状态。因为拦截器是全局共享的,可能会被多个请求同时访问。 3. 尽量避免在拦截器中进行大量的计算或IO操作,这样会降低性能。如果有需要,可以将这些逻辑移动到处理函数中。

实现一个校验token的请求拦截器

拦截器:

/*
	token格式:
		"phone": phone,
		"exp":   time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间为一天
*/

// UserKey is the key used to store user info in the context
const UserKey = "user"

// TokenIntercept Token校验拦截器
func TokenIntercept() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("Authorization")
		claims, err := utils.ParseJwt(token) // 解析token
		if err != nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) // 发生错误时结束本次拦截器处理
			return                                                      // 结束本次请求处理
		}
		// 校验是否过期
		exp, _ := claims.GetExpirationTime()
		if !exp.After(time.Now()) {
			c.AbortWithStatusJSON(http.StatusUnauthorized, "登录过期")
			return
		}
		// 获取用户信息并存储到context中
		phone, _ := claims.GetSubject()
		u := user.SelectUserByPhone(phone)
		c.Set(UserKey, u)

		// 更新token过期时间并设置响应头
		newToken, _ := utils.GenerateJwt(jwt.MapClaims{
			"phone": phone,
			"exp":   time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间为一天
		})
		c.Header("Authorization", newToken)

		// 继续处理请求
		c.Next()
	}
}

测试:

func testConnect() {
	r.GET(Pre+"test", intercept.TokenIntercept(), func(c *gin.Context) {
		c.String(http.StatusOK, "Token校验通过")
	})
}

在这里插入图片描述
在这里插入图片描述

关于Next()&Abort()

Abort()

c.Abort() 是 Gin 框架中 *gin.Context 的一个方法,它的作用是立即终止当前的中间件链处理,并阻止执行后续的中间件或路由处理器。这通常用在中间件中,当你需要基于某些条件(比如认证失败、请求参数不正确等)提前结束请求处理流程时。

以下是 c.Abort() 方法的一些关键点:

  1. 终止处理: 当调用 c.Abort() 后,当前正在执行的中间件会立即停止处理,并且不会执行任何后续的中间件或处理器。
  2. 不写入响应: 默认情况下,c.Abort() 不会向客户端发送任何响应。如果你想在终止请求的同时发送响应,可以使用 c.AbortWithStatus()c.AbortWithStatusJSON() 等变体。
  3. 保持请求上下文: 尽管处理被终止,但请求的上下文(*gin.Context)仍然保持有效,你可以在调用 c.Abort() 后访问和修改它,例如设置响应头或状态码。
  4. 错误处理: 通常在调用 c.Abort() 之前,你会设置错误处理逻辑,比如记录日志、发送错误响应等。
  5. No-op 在路由处理器中: 如果在路由处理器中调用 c.Abort(),它不会影响其他路由处理器的执行,但会立即终止当前处理器的执行。
Next()

在 Gin 框架中,c.Next()*gin.Context 的一个方法,它用于调用中间件链中的下一个中间件或路由处理器。c.Next() 通常在中间件中使用,允许中间件在执行完自己的逻辑后,将控制权传递给下一个处理函数。

以下是 c.Next() 方法的一些关键点:

  1. 继续执行: c.Next() 调用后,Gin 框架会继续执行中间件链中的下一个中间件或路由处理器。
  2. 执行顺序: 在中间件链中,c.Next() 决定了执行的顺序。调用 c.Next() 后,Gin 会按照定义的顺序执行下一个中间件。
  3. 可以在任何中间件中调用: 你可以在中间件链中的任何一个中间件里调用 c.Next(),以继续执行链中的下一个中间件。
  4. 可以多次调用: 虽然通常每个中间件只调用一次 c.Next(),但在某些情况下,你可能需要根据条件逻辑多次调用 c.Next()
  5. c.Abort() 相对: 当你调用 c.Abort() 时,当前中间件链的执行会被终止,而 c.Next() 则是用来继续执行中间件链。
  6. 在路由处理器中无效: 在路由处理器中调用 c.Next() 是无效的,因为路由处理器不是中间件链的一部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值