Gin 中间件的配置方式有许多种,大致为:
- 在
Handle()、Any()、Match()、GET()等基本路由方法中配置HandlerFunc。 - 在
RouterGroup的Group()方法中配置HandlerFunc。 - 使用
Use()方法配置HandlerFunc。
特定请求的中间件
为特定请求配置中间件,可以直接在请求路由的 HandlerFunc 参数中配置。例如:
-
controllers/user:1type userController struct{} 2 3var UserController userController 4 5func (userController) GetUser(ctx *gin.Context) { 6 id := ctx.Param("id") 7 // 获取 Context 内部字典的值 8 token, exists := ctx.Get(middlewares.TOKEN) 9 if !exists { 10 ctx.JSON(http.StatusBadRequest, gin.H{"error": "Unauthorized..."}) 11 } 12 ctx.PureJSON(http.StatusOK, gin.H{ 13 "id": id, "username": "zhangsan", "token": token, 14 }) 15} -
middlewares/user:1type userMiddlewares struct{} 2 3var UserMiddlewares userMiddlewares 4 5const Authorization = "Authorization" 6 7func (userMiddlewares) VerifyToken(ctx *gin.Context) { 8 auth := strings.TrimSpace(ctx.GetHeader(Authorization)) 9 if auth == "Bearer 123456" { 10 // 使用 Context.Set() 设置键值对 11 // 这个键值对只能在当前处理链中按顺序流转 12 // 换句话说,这个键值对只是存储在当前 Context 中 13 ctx.Set(TOKEN, "123456") 14 ctx.Next() 15 } else { 16 ctx.JSON(http.StatusBadRequest, gin.H{"error": "Unauthorized..."}) 17 ctx.Abort() 18 } 19}中间件也可专门为其定义一个
middlewares包来存放项目中所有的中间件。处理链:即一组按顺序执行的中间件和处理函数。
-
routers/user.go:将中间函数和处理函数绑定到路由上:
1func UserRoutersInit(engin *gin.Engine) { 2 engin.Group("/user", middlewares.UserMiddleware). 3 GET("/:id", middlewares.UserMiddlewares.VerifyToken, 4 controllers.UserController.GetUser) 5}
全局中间件
全局中间件就是绑定在根路由上的、全局生效的中间件,可以使用 RouterGroup.Use() 方法绑定全局中间件。例如:
1func Router() (engine *gin.Engine) {
2 engine = gin.Default()
3 // 设置全局中间件
4 engine.Use(middlewares.GlobalMiddlewares.VerifyToken)
5
6 // ...
7}
如果需要绑定的中间件过多,除了多次调用 User() 方法绑定或在 User() 方法中传入多个中间件外,还能使用 gin.HandlersChain 类型。gin.HandlersChain 的定义如下:
1type HandlersChain []HandlerFunc
使用 gin.HandlersChain 来聚集多个中间件,然后将它们一次性传入 User() 中:
1var globalMiddlewares = gin.HandlersChain{
2 middlewares.GlobalMiddlewares.VerifyToken,
3 middlewares.GlobalMiddlewares.VerifyAuthorization,
4 // ...
5}
6
7func Router() (engine *gin.Engine) {
8 engine = gin.Default()
9 // 设置全局中间件
10 engine.Use(globalMiddlewares...)
11
12 // ...
13}
通过使用 gin.HandlersChain 聚集并注册全局中间件后,中间函数的执行顺序将按照 gin.HandlersChain 中的顺序执行。
分组中间件
分组中间件同样可以使用 RouterGroup.Use() 方法,不同的是调用方法的对象是具体的路由分组对象,也就是 Engin.Group() 调用之后返回的对象。例如:
1engin.Group("/user").
2 Use(middlewares.UserMiddlewares.VerifyToken)
除了使用 RouterGroup.Use() 方法,分组中间件还可以在 Engin.Group() 调用的时候注册。例如:
1engin.Group("/user", middlewares.UserMiddlewares.VerifyToken)
同样地,也可以使用 gin.HandlersChain:
1var groupMiddlewares = gin.HandlersChain{
2 middlewares.UserMiddlewares.VerifyToken,
3 // ...
4}
5
6engin.Group("/user", ...groupMiddlewares)
注:
User()方法注册的中间件,和使用其它路由方法(如Handle()、Any()、GET()、Match()等等)注册的中间件、处理函数它们之间的调用顺序也是按照注册的顺序执行。例如:1engin.Group("/user", middlewares.UserMiddlewares.VerifyToken). 2 GET("/info/:username", controllers.UserController.GetInfo). 3 POST("/list", controllers.UserController.GetList). 4 // 在此之前的路由,在执行时不会调用 middlewares.UserMiddlewares.VerifyAuthorization 5 // 只会调用 middlewares.UserMiddlewares.VerifyToken 6 Use(middlewares.UserMiddlewares.VerifyAuthorization). 7 // 在此之前的路由,在执行时会按顺序调用 middlewares.UserMiddlewares.VerifyToken 8 // 和 middlewares.UserMiddlewares.VerifyAuthorization 9 GET("/:id", controllers.UserController.GetUser)
中间件 Context
在中间件中,Context 有一些方法可用于中间件的编写:
-
Context.Next():将控制权传递给下一个中间件或处理函数。1func MyMiddleware(c *gin.Context) { 2 // 执行某些预处理逻辑 3 c.Next() 4 // 执行某些后处理逻辑 5} -
Context.Abort():立即停止执行后续的中间件和处理函数,可以用来处理错误或特殊请求。1func ErrorMiddleware(c *gin.Context) { 2 if c.Query("error") == "true" { 3 c.Abort() // 停止执行后续的中间件和处理函数 4 } else { 5 c.Next() 6 } 7} -
Context.Set()和Context.Get():在当前请求处理链中存储和检索数据,允许在处理链中不同的中间件和处理函数之间共享信息。1func StoreDataMiddleware(c *gin.Context) { 2 // 存储数据 3 c.Set("user_id", 123) 4 c.Next() 5} 6 7func RetrieveDataMiddleware(c *gin.Context) { 8 // 获取数据 9 userId, err := c.Get("user_id") 10 // 使用 userId ... 11}Context.Set()和Context.Get()存储和获取的数据,是存储在当前Context对象的内部字典(store字段)中。在 Gin 中,对于一条处理链中所有的中间件和处理函数共享同一个Context对象。
Contex中除了上述的 4 个方法外,还有其它可以用于控制中间件或处理函数执行流的方法。例如以Abort开头的Abort系方法,它们在终止执行链执行的同时,还提供了其它一些额外的操作。比如说Context.AbortWithStatus(),在中止执行链的同时设置响应状态码。
评论