【注意】最后更新于 March 17, 2023,文中内容可能已过时,请谨慎使用。
中间件(英语: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 {
// 打印debug信息
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()
// 添加一个中间件: Logger()
engine.GET("/route",gin.Logger(), func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"针对单个路由"})
})
// 添加多个中间件: Logger(),Recovery()
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()
// 声明路由后,通过Use注册中间件
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()
// context.Abort() 停止往下执行
// 请求后业务逻辑...
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()
// context.Abort() 停止往下执行
// 请求后
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
|
// 自定义中间件A
func MyMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
// 请求前
fmt.Println("中间件1-- 请求前")
context.Next()
// 请求后
fmt.Println("中间件1-- 请求后")
}
}
// 自定义中间件B
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 源码
./main.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"
)
// 检测token
func CheckTokenMiddle() gin.HandlerFunc {
return func(context *gin.Context) {
// 获取token
token := context.DefaultQuery("token","")
// 检测token
if token != "abcd" {
context.JSON(500,gin.H{"msg":"请先登录!"})
// 终止请求
context.Abort()
}
// token合法则继续往下执行
context.Next()
}
}
// 启动服务
func RunServeWithCheckToken() {
engine := gin.Default()
// 登录接口不需要添加检测Token中间件
engine.GET("/user/login", func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"登录成功!","token":"abcd"})
})
// 需要验证token的路由
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
|
# 不需要验证token的接口
➜ curl -X GET http://127.0.0.1:8080/user/login
{"msg":"登录成功!","token":"abcd"}
# 需要验证Token,但是没传时
➜ curl -X GET http://127.0.0.1:8080/user/info
{"msg":"请先登录!"}
# 需要验证Token,并且已传时
➜ 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
|
// 在中间件中使用Goroutine
func main() {
engine := gin.Default()
engine.Use(MyMiddleWithGoRoutine())
engine.GET("/useGo", func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"success"})
})
_ = engine.Run()
}
// 在中间件中使用Goroutine
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)库中整理很多常用的中间件,可以直接使用,具体中间件有以下: