GO常用web框架之gin学习笔记(4)| 青训营笔记

180 阅读3分钟

[ go 与 golang | 青训营笔记]

这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天, 在学习了go的相关基础知识以后,可以说是初步的了解了go,接下来,我们将步入全新的环节,今天我们学习go的一个常用web框架gin

8. 中间件

Gin中的中间件必须是一个gin.HandlerFunc类型

单独注册中间件

import (
  "fmt"
  "github.com/gin-gonic/gin"
  "net/http"
)
func indexHandler(c *gin.Context) {
  fmt.Println("index.....")
  c.JSON(http.StatusOK, gin.H{
    "msg": "index",
  })
}

//定义一个中间件
func m1(c *gin.Context) {
  fmt.Println("m1 in.........")
}
func main() {
  r := gin.Default()
  //m1处于indexHandler函数的前面,请求来之后,先走m1,再走index
  r.GET("/index", m1, indexHandler)

  _ = r.Run()
}

多个中间件

router.GET,后面可以跟很多HandlerFunc方法,这些方法其实都可以叫中间件

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

/*
m1  ...in
index ...
m2  ...in
*/

中间件拦截响应

c.Abort()拦截,后续的HandlerFunc就不会执行了

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.JSON(200, gin.H{"msg": "第一个中间件拦截了"})
  c.Abort()
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...")
    c.JSON(200, gin.H{"msg": "响应数据"})
  }, m2)

  router.Run(":8080")
}

中间件放行

c.Next(),Next前后形成了其他语言中的请求中间件和响应中间件

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m1(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Next()
  fmt.Println("m1 ...out")
}
func m2(c *gin.Context) {
  fmt.Println("m2 ...in")
  c.Next()
  fmt.Println("m2 ...out")
}

func main() {
  router := gin.Default()

  router.GET("/", m1, func(c *gin.Context) {
    fmt.Println("index ...in")
    c.JSON(200, gin.H{"msg": "响应数据"})
    c.Next()
    fmt.Println("index ...out")
  }, m2)

  router.Run(":8080")
}

/*
m1 ...in
index ...in
m2 ...in   
m2 ...out  
index ...out
m1 ...out
*/

如果其中一个中间件响应了c.Abort(),后续中间件将不再执行,直接按照顺序走完所有的响应中间件

全局注册中间件

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m10(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Next()
  fmt.Println("m1 ...out")
}

func main() {
  router := gin.Default()

  router.Use(m10)
  router.GET("/", func(c *gin.Context) {
    fmt.Println("index ...in")
    c.JSON(200, gin.H{"msg": "index"})
    c.Next()
    fmt.Println("index ...out")
  })

  router.Run(":8080")

}

使用Use去注册全局中间件,Use接收的参数也是多个HandlerFunc

中间件传递数据

使用Set设置一个key-value,

在后续中间件中使用Get接收数据

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func m10(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Set("name", "fengfeng")
}

func main() {
  router := gin.Default()

  router.Use(m10)
  router.GET("/", func(c *gin.Context) {
    fmt.Println("index ...in")
    name, _ := c.Get("name")
    fmt.Println(name)
    
    c.JSON(200, gin.H{"msg": "index"})
  })

  router.Run(":8080")

}

value的类型是any类型,所有我们可以用它传任意类型,在接收的时候做好断言即可

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

type User struct {
  Name string
  Age  int
}

func m10(c *gin.Context) {
  fmt.Println("m1 ...in")
  c.Set("name", User{"ranran", 21})
  c.Next()
  fmt.Println("m1 ...out")
}

func main() {
  router := gin.Default()

  router.Use(m10)
  router.GET("/", func(c *gin.Context) {
    fmt.Println("index ...in")
    name, _ := c.Get("name")
    user := name.(User)
    fmt.Println(user.Name, user.Age)
    c.JSON(200, gin.H{"msg": "index"})
  })

  router.Run(":8080")

}

路由分组

将一系列的路由放到一个组下,统一管理

例如,以下的路由前面统一加上api的前缀

package main

import "github.com/gin-gonic/gin"

func main() {
  router := gin.Default()

  r := router.Group("/api")
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run(":8080")
}

路由分组注册中间件

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func middle(c *gin.Context) {
  fmt.Println("middle ...in")
}

func main() {
  router := gin.Default()

  r := router.Group("/api").Use(middle)  // 可以链式,也可以直接r.Use(middle)
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run(":8080")
}

这样写我们就可以指定哪一些分组下可以使用中间件了

当然,中间件还有一种写法,就是使用函数加括号的形式

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

func middle(c *gin.Context) {
  fmt.Println("middle ...in")
}
func middle1() gin.HandlerFunc {
  // 这里的代码是程序一开始就会执行
  return func(c *gin.Context) {
    // 这里是请求来了才会执行
    fmt.Println("middle1 ...inin")
  }
}

func main() {
  router := gin.Default()

  r := router.Group("/api").Use(middle, middle1())
  r.GET("/index", func(c *gin.Context) {
    c.String(200, "index")
  })
  r.GET("/home", func(c *gin.Context) {
    c.String(200, "home")
  })

  router.Run(":8080")
}

gin.Default

func Default() *Engine {
  debugPrintWARNINGDefault()
  engine := New()
  engine.Use(Logger(), Recovery())
  return engine
}

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

Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。 Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。 如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

使用gin.New,如果不指定日志,那么在控制台中就不会有日志显示

中间件案例