目录
概述
中间件就是匹配路由前和匹配路由完成后执行的一系列操作。Gin框架允许开发者在处理请求的过程中,加入自己的钩子函数(Hook)。这个钩子函数就叫中间件。
中间件适合处理一些公共的业务逻辑,比如登陆认证、权限校验、数据分页、记录日志、耗时统计。
使用和不使用默认中间件
在使用gin.Default()创建Gin引擎时,已经自带了Logger()、Recovery()中间件。
- Logger中间件将日志写入gin.DefaultWriter。
- Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。
如果创建Gin引擎时,不想使用默认的中间件。可以使用gin.New()。但是不建议这么做。
自定义中间件
目录结构
project
|--main.go
|--routers(目录)
|--routerGroupInit.go
|--userRouterGroup.go
|--controllers(目录)
|--user(目录)
|--userControllers.go
|--middileware(目录)
|--simpleMiddleware.go
定义中间件-simpleMiddleware.go
其实语法就和写控制器一样。
package mddileware
import (
"fmt"
"github.com/gin-gonic/gin"
)
func SimpleMiddleware(c *gin.Context) {
fmt.Println("SimpleMiddleware")
}
ctx.Next()、ctx.Abort()
1.ctx.Next()
调用请求的剩余处理程序,常用于计算程序执行时间。
package mddileware
import (
"fmt"
"github.com/gin-gonic/gin"
)
func SimpleMiddleware(c *gin.Context) {
fmt.Println("SimpleMiddleware")
c.Next()
fmt.Println("SimpleMiddleware--2")
}
此代码先执行打印“SimpleMiddleware”;然后到c.Next()时会向后调用路由中定义的其他控制器;等向后所有控制器都执行完后,再打印“SimpleMiddleware-2”。
2.ctx.Abort()
终止请求剩余的控制器。
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
)
func SimpleMiddleware(c *gin.Context) {
fmt.Println("SimpleMiddleware")
c.Abort()
fmt.Println("SimpleMiddleware--2")
}
此代码先执行打印“SimpleMiddleware”;然后到c.Abort()时不再调用后续的中间件和控制器;再打印“SimpleMiddleware-2”。
中间件和控制器间传值
//设置数据
ctx.Set("keyname", "value")
//获取数据
vname, _ := ctx.Get("keyname")
注意:这里获取到的vname是一个空接口类型(interface{})。使用类型断言转为其他类型,如:
v, _ := vname.(string) 转为字符串类型
在中间件中使用goroutine
当在中间件或者handler中使用goroutine时,不能直接使用原始的上下文(c *gin.Context)。必须使用c.Copy()创建其只读副本,使用只读副本。
package middleware
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func SimpleMiddleware(c *gin.Context) {
cCp := c.Copy()
go func() {
time.Sleep(time.Second * 2)
fmt.Println("Path:" + cCp.Request.URL.Path)
}()
c.JSON(http.StatusOK, gin.H{
"msg": "SimpleMiddleware-1",
})
}
配置使用中间件
路由中使用中间件
路由方法中,我们是可以传入多个控制器的。如果传入多个自定义控制器,则最后一个控制器是实际处理业务逻辑,前面的控制器都是中间件。
package routers
import (
"hello/controllers/user"
"hello/middleware"
"github.com/gin-gonic/gin"
)
func UserRouterGroupInit(r *gin.Engine) {
userGroup := r.Group("/user")
{
userGroup.GET("/userInfo", middleware.SimpleMiddleware, user.UserController{}.GetUserInfo)
userGroup.POST("/userAdd", middleware.SimpleMiddleware, user.UserController{}.PostUserAdd)
}
}
路由组中使用中间件
路由组配置中间件有两种方法
方法一:
r.Group("path", middleware, ...)
package routers
import (
"hello/controllers/user"
"hello/middleware"
"github.com/gin-gonic/gin"
)
func UserRouterGroupInit(r *gin.Engine) {
userGroup := r.Group("/user", middleware.SimpleMiddleware)
{
userGroup.GET("/userInfo", user.UserController{}.GetUserInfo)
userGroup.POST("/userAdd", user.UserController{}.PostUserAdd)
}
}
方法二:
RouterGroup.Use(middleware, ...)
package routers
import (
"hello/controllers/user"
"hello/middleware"
"github.com/gin-gonic/gin"
)
func UserRouterGroupInit(r *gin.Engine) {
userGroup := r.Group("/user")
userGroup.Use(middleware.SimpleMiddleware)
{
userGroup.GET("/userInfo", user.UserController{}.GetUserInfo)
userGroup.POST("/userAdd", user.UserController{}.PostUserAdd)
}
}
全局使用中间件
route.Use()方法,定义全局中间件。同样可以使用逗号分隔,定义多个中间件。
package main
import (
"hello/middleware"
"hello/routers"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(middleware.SimpleMiddleware)
routers.RouterGroupInit(r)
r.Run(":8080")
}