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,由于该索引不存在,所以队列中后面的的中间件函数和逻辑处理函数就不会执行了。

posted @ 2022-02-12 12:19  专职  阅读(497)  评论(0编辑  收藏  举报