中间件(Middleware),又译 中间件、中介层,是一类提供系统软件和应用软件之间连接、便于软件各部件之间的沟通的软件,应用软件可以借助中间件在不同的技术架构之间共享信息与资源。
1.介绍
在Gin框架中,中间件本质上是gin.HandlerFunc 函数,如果我们要自定义中间件,只需要返回类型是gin.HandlerFunc 即可。
在Gin框架中,使用中间件可分为以下几种场景:
2.使用
2.1 全局使用
在gin.Default()函数中,默认注册全局中间件Logger、Recovery,具体代码如下:
1 2 3 4 5 6 7 8 9
   | func Default() *Engine {    	debugPrintWARNINGDefault()    	engine := New()    	engine.Use(Logger(), Recovery()) 	return engine }
 
  | 
 
2.2 单个路由使用
1 2 3 4 5 6 7 8 9 10 11 12
   | func main()  { 	engine := gin.New() 	 	engine.GET("/route",gin.Logger(), func(context *gin.Context) { 		context.JSON(200,gin.H{"msg":"针对单个路由"}) 	}) 	 	engine.GET("/route2",gin.Logger(),gin.Recovery(), func(context *gin.Context) { 		context.JSON(200,gin.H{"msg":"针对单个路由添加多个中间件"}) 	}) 	_ = engine.Run() }
 
  | 
 
2.3 路由组使用
1 2 3 4 5 6 7 8 9 10 11
   | func main()  { 	engine := gin.New()    	v1Group := engine.Group("/v1").Use(gin.Logger()) 	{ 		v1Group.GET("/middleGroup", func(context *gin.Context) { 			 context.JSON(200,gin.H{"msg":"succss"}) 		}) 	} 	_ = engine.Run() }
 
  | 
 
3.自定义中间件
我们可以创建自己的中间件,在中间件中需要继续执行时,使用c.Next(),而要终止执行时则需调用c.Abort()终止请求。
3.1 语法结构
1 2 3 4 5 6 7 8 9 10 11
   | func MiddleName() gin.HandlerFunc { 	return func(context *gin.Context) { 		      		context.Next()      		 		end := time.Now().Unix() 		fmt.Printf("接口耗时:%v 秒 \n", end - t) 	} }
 
  | 
 
代码说明:
MiddleName: 自定义中间件名称。
 
gin.HandlerFunc: 中间件始终返回这个类型。
 
func(context *gin.Context): 匿名函数,参数是上下文(context *gin.Context)
 
context.Next(): 继续执行调用这个函数。
 
context.Abort():终止执行调用这个函数。
 
3.2 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   |  func MyMiddleware() gin.HandlerFunc { 	return func(context *gin.Context) { 		 		fmt.Println("中间件-- 请求前") 		t := time.Now().Unix()      		context.Next()      		 		end := time.Now().Unix() 		fmt.Printf("接口耗时:%v 秒 \n", end - t) 	} }
  func RunWithMyMiddle()  { 	engine := gin.New() 	 	engine.GET("/route",MyMiddleware(), func(context *gin.Context) { 		time.Sleep(time.Second * 3) 		context.JSON(200,gin.H{"msg":"自定义路由"}) 	}) 	_ = engine.Run() }
 
  | 
 
4.多个中间件
如果同时注册多个中间件,那么他们之间的执行顺序是什么样呢?
4.1 执行流程

4.2 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   |  func MyMiddleware() gin.HandlerFunc { 	return func(context *gin.Context) { 		 		fmt.Println("中间件1-- 请求前") 		context.Next() 		 		fmt.Println("中间件1-- 请求后") 	} }
  func MyMiddleware2() gin.HandlerFunc { 	return func(context *gin.Context) { 		 		fmt.Println("中间件2-- 请求前") 		context.Next() 		 		fmt.Println("中间件2-- 请求后") 	} }
  func main()  { 	engine := gin.New() 	 	engine.GET("/route",MyMiddleware(), MyMiddleware2(),func(context *gin.Context) { 		time.Sleep(time.Second * 3) 		context.JSON(200,gin.H{"msg":"自定义路由"}) 	}) 	_ = engine.Run() }
 
  | 
 
请求控制台输出:
1 2 3 4
   | 中间件1-- 请求前 中间件2-- 请求前 中间件2-- 请求后 中间件1-- 请求后
 
  | 
 
5.实践
创建自定义中间件用来判断Token是否有效(是否是登录状态)。
5.1 源码
./mian.go代码:
1 2 3 4 5
   | package main import "go-use/practise" func main() { 	practise.RunServeWithCheckToken() }
 
  | 
 
./practise/check_token.go 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | package practise import ( 	"github.com/gin-gonic/gin" )
  func CheckTokenMiddle() gin.HandlerFunc { 	return func(context *gin.Context) { 		 		token := context.DefaultQuery("token","") 		 		if token != "abcd" { 			context.JSON(500,gin.H{"msg":"请先登录!"}) 			 			context.Abort() 		} 		 		context.Next() 	} }
  func RunServeWithCheckToken()  { 	engine := gin.Default() 	 	engine.GET("/user/login", func(context *gin.Context) { 		context.JSON(200,gin.H{"msg":"登录成功!","token":"abcd"}) 	}) 	 	user := engine.Group("/user").Use(CheckTokenMiddle()) 	{ 		 		user.GET("/info", func(context *gin.Context) { 			data := map[string]interface{} { 				"name": "张三", 				"age": 18, 				"likes": []string{"打游戏","旅游"}, 			} 			context.JSON(200,gin.H{"msg":"请求成功","data":data}) 		}) 		 		user.GET("/update", func(context *gin.Context) { 			context.JSON(200,gin.H{"msg":"请求成功"}) 		}) 	} 	 	_ = engine.Run() }
 
  | 
 
5.2 请求
1 2 3 4 5 6 7 8 9
   |  [lepeng@centos ~] {"msg":"登录成功!","token":"abcd"}
  [lepeng@centos ~] {"msg":"请先登录!"}
  ➜  curl -X GET http://127.0.0.1:8080/user/info?token=abcd {"data":{"age":18,"likes":["打游戏","旅游"],"name":"张三"},"msg":"请求成功"}
 
  | 
 
6.使用Goroutine
当在中间件或 handler 中启动新的 Goroutine 时,不能使用原始的上下文,必须使用只读副本。
6.1 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   |  func main()  { 	engine := gin.Default() 	engine.Use(MyMiddleWithGoRoutine()) 	engine.GET("/useGo", func(context *gin.Context) { 		context.JSON(200,gin.H{"msg":"success"}) 	}) 	_ = engine.Run() }
  func MyMiddleWithGoRoutine() gin.HandlerFunc { 	return func(context *gin.Context) { 		 		cpContext := context.Copy() 		go func() { 			time.Sleep(3 * time.Second) 			fmt.Println(cpContext.Request.URL.Path) 		}() 		context.Next() 	} }
 
  | 
 
7.流行的中间件
在gin-gonic/contrib(https://github.com/gin-gonic/contrib)库中整理很多常用的中间件,可以直接使用,具体中间件有以下:
