Gin中间件| 青训营笔记

114 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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")
}

定义了两个中间件,它们的执行流程应该是这样的:

image.png

输出结果为:

image.png

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()去创建一个不带任何中间件的路由引擎。