gin中间件详解及原理分析
gin中间件详解
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func main() {
r := gin.New()
// 使用Logger和Recovery中间件
r.Use(gin.Logger())
r.Use(gin.Recovery())
// 使用自定义中间件,中间件既可以应用在某个url上,也可以应用在某个组上,也可以应用在全局所有url上
authorized := r.Group("/admin", TokenRequired(), MyLogger())
authorized.GET("/home", func(context *gin.Context) {
time.Sleep(time.Millisecond * 380)
v, _ := context.Get("example")
context.JSON(http.StatusOK, gin.H{"message": "home", "example": v})
})
r.GET("/index", func(context *gin.Context) {
time.Sleep(time.Millisecond * 1380)
context.JSON(http.StatusOK, gin.H{"message": "index"})
})
r.Run()
}
func MyLogger() gin.HandlerFunc {
// 统计执行时长中间件
return func(context *gin.Context) {
start := time.Now()
context.Set("example", "123456") // 中间件中添加变量值
// 让原本该执行的逻辑继续执行
context.Next()
fmt.Println(time.Since(start))
// 获取函数执行完后的状态
status := context.Writer.Status()
fmt.Printf("状态:%v\n", status)
}
}
func TokenRequired() gin.HandlerFunc {
return func(context *gin.Context) {
//token := context.GetHeader("x-token")
// 获取token
var token string
for key, value := range context.Request.Header {
if key == "X-Token" {
token = value[0]
break
}
}
// 判定token
if token != "x-sankuan"{
context.JSON(http.StatusUnauthorized, gin.H{
"err": "unauthorized",
"detail": "token认证失败",
})
// return 挑战,为什么return都阻止不了后续逻辑的执行
context.Abort() // 终止中间件后续逻辑的执行
}
context.Next()
}
}
原理分析
因为gin的中间件函数与业务逻辑处理函数是放到gin的队列中的
所以当一个中间件函数执行return语句时只代表当前中间件函数执行完了,gin框架会驱动index++,然后执行队列中后续的中间件函数或逻辑处理函数
当在中间件函数中执行context.Next()时,源码如下
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
队列中的index会执行+1操作,然后调用c.handlersc.index,也就是调用队列中后面的一个函数,
当执行context.Abort()时,源码如下:
func (c *Context) Abort() {
c.index = abortIndex
}
会修改c.index = 63.5,由于该索引不存在,所以队列中后面的的中间件函数和逻辑处理函数就不会执行了。