Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

1 单个路由注册中间件

  1. // 统计耗时 的中间件
  2. func m1(c *gin.Context) {
  3. fmt.Println("m1 in ...")
  4. // 计时
  5. start := time.Now()
  6. c.Next() // 调用后续的处理函数
  7. // c.Abort() // 阻止调用后续的处理函数
  8. cost := time.Since(start)
  9. fmt.Printf("cost:%v\n", cost)
  10. fmt.Println("m1 out ...")
  11. }
  12. func indexHandler(c *gin.Context) {
  13. c.JSON(http.StatusOK, "index")
  14. }
  15. func main() {
  16. r := gin.Default()
  17. r.GET("/index", m1, indexHandler)
  18. r.Run("0.0.0.0:5000")
  19. }

2 全局路由注册中间件

(1) 全局注册中间件函数m1, m2

  1. // 统计耗时 的中间件
  2. func m1(c *gin.Context) {
  3. fmt.Println("m1 in ...")
  4. // 计时
  5. start := time.Now()
  6. c.Next() // 调用后续的一个处理函数
  7. // c.Abort() // 阻止调用后续的所有处理函数
  8. cost := time.Since(start)
  9. fmt.Printf("cost:%v\n", cost)
  10. fmt.Println("m1 out ...")
  11. }
  12. func m2(c *gin.Context) {
  13. fmt.Println("m2 in ...")
  14. c.Next()
  15. fmt.Println("m2 out ...")
  16. }
  17. func indexHandler(c *gin.Context) {
  18. fmt.Println("index")
  19. c.JSON(http.StatusOK, "index")
  20. }
  21. func main() {
  22. r := gin.Default()
  23. r.Use(m1, m2) // 全局注册中间件函数m1, m2
  24. r.GET("/index", indexHandler)
  25. r.Run("0.0.0.0:5000")
  26. }

(2) 闭包

  1. func login_required(doCheck bool) gin.HandlerFunc {
  2. return func(c *gin.Context) {
  3. if doCheck {
  4. // 获取token
  5. // 查询数据库获取user_id
  6. c.Set("user_id", 23)
  7. } else {
  8. c.Next()
  9. }
  10. }
  11. }
  12. func indexHandler(c *gin.Context) {
  13. fmt.Println("index")
  14. user_id, exists := c.Get("user_id")
  15. if !exists {
  16. fmt.Println("user_id不存在")
  17. } else {
  18. fmt.Println("user_id:", user_id)
  19. }
  20. c.JSON(http.StatusOK, "index")
  21. }
  22. func main() {
  23. r := gin.Default()
  24. r.Use(login_required(true))
  25. r.GET("/index", indexHandler)
  26. r.Run("0.0.0.0:5000")
  27. }

3 路由组注册中间件

  1. func login_required(doCheck bool) gin.HandlerFunc {
  2. return func(c *gin.Context) {
  3. if doCheck {
  4. // 获取token
  5. // 查询数据库获取user_id
  6. c.Set("user_id", 23)
  7. } else {
  8. c.Next()
  9. }
  10. }
  11. }
  12. func main() {
  13. r := gin.Default()
  14. fooGroup := r.Group("/foo", login_required(true))
  15. {
  16. fooGroup.GET("/bar", func(c *gin.Context){
  17. user_id, exists := c.Get("user_id")
  18. if !exists {
  19. fmt.Println("user_id不存在")
  20. } else {
  21. fmt.Println("user_id:", user_id)
  22. }
  23. c.JSON(http.StatusOK, "foobar")
  24. })
  25. }
  26. r.Run("0.0.0.0:5000")
  27. }

4 注意事项

gin.Default()默认使用了Logger和Recovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。