这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天
零、 前言
本期介绍Gin的中间件,开发字节跳动青训营的抖音后端势必会用到Gin的中间件,比如可能需要编写jwt中间件进行认证,编写中间件对用户创建账号输入的密码进行加密等等,于是写下这篇笔记。
一、 内容全览
- Gin中间件
- 定义Gin中间件
- 授权中间件
- 注意事项
二、 内容详解
首先还是简单介绍一下Gin中间件,Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子函数,也就是hook。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等等,中间件适合用来编写一些公共业务。
2.1 定义Gin中间件
首先Gin中间件必须是gin.HandlerFunc,下面是一个记录处理请求耗时的中间件示例:
// StatCost 是一个统计请求耗时的中间件
func StatCost() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Set("name", "坤坤") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
// 调用该请求的剩余处理程序
c.Next()
// 不调用该请求的剩余处理程序
// c.Abort()
// 计算耗时
cost := time.Since(start)
log.Println(cost)
}
}
就像示例里写的,我们可以调用c.Next()来执行剩余的处理函数,也可以调用c.Abort()来阻止后续处理函数执行。函数的处理流程类似递归,请注意以下示例程序:
func m1(c *gin.Context) {
fmt.Println("m1 in...")
c.Next() // 调用后续的处理函数
//c.Abort() // 阻止调用后续的处理函数
fmt.Println("m1 out")
}
func m2(c *gin.Context) {
fmt.Println("m2 in...")
c.Next()
//c.Abort()
fmt.Println("m2 out")
}
func main() {
r := gin.Default() // Default默认使用了中间件Logger()和Recovery()
r.Use(m1, m2) // r全局使用中间件函数m1和m2
r.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.Run(":9090")
}
定义了两个中间件,它们的执行流程应该是这样的:
输出结果为:
2.2 授权中间件
在gin框架中,可以为每个路由授权任意数量的中间件,同样使用于路由组
可以调用Use(Func)为全局路由设置中间件:
func main() {
// 新建一个没有任何默认中间件的路由
r := gin.New()
// 注册一个全局中间件
r.Use(StatCost())
r.GET("/test", func(c *gin.Context) {
name := c.MustGet("name").(string) // 从上下文取值
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"message": "Hello world!",
})
})
r.Run()
}
也可以将中间件授权给单个路由:
// 给/test2路由单独注册中间件(可注册多个)
r.GET("/test2", StatCost(), func(c *gin.Context) {
name := c.MustGet("name").(string) // 从上下文取值
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"message": "Hello world!",
})
})
也可以为一个路由组授权中间件,
shopGroup := r.Group("/shop", StatCost())
// 也可以这样写:
// shopGroup := r.Group("/shop")
// shopGroup.Use(StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
2.3 注意事项
我们习惯性的调用r := gin.Default()去创建一个默认的路由引擎,其实这个路由引擎加了两个中间件:Logger()和Recovery(),这是gin的源码:
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
如果不想用这两个中间件可以调用gin.New()去创建一个不带任何中间件的路由引擎。