Gin框架中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
一、定义中间件
Gin中的中间件必须是一个gin.HandlerFunc
类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start :=time.Now()
c.Next() // 调用后续处理的函数
//c.Abort() // 阻止调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func main() {
r := gin.Default()
r.GET("/index", m1, index)
r.Run(":9999")
}
二、定义局部中间件
在每次请求中添加中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start :=time.Now()
c.Next() // 调用后续处理的函数
//c.Abort() // 阻止调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func main() {
r := gin.Default()
r.GET("/index", m1, index)
// 局部中间件
r.GET("/home", m1, func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg":"home",
})
})
r.GET("/add", m1, func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg":"add",
})
})
r.Run(":9999")
}
三、定义全局中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start := time.Now()
c.Next() // 调用后续处理的函数
//c.Abort() // 阻止调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func main() {
r := gin.Default()
// 全局注册中间
r.Use(m1)
r.GET("/index", index)
// 局部中间件
r.GET("/home", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "home",
})
})
r.GET("/add", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "add",
})
})
r.Run(":9999")
}
三、定义多个中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start := time.Now()
c.Next() // 调用后续处理的函数
//c.Abort() // 阻止调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
c.Next() // 调用后续处理的函数
fmt.Println("m2 out ....")
}
func main() {
r := gin.Default()
// 全局注册中间
r.Use(m1, m2)
r.GET("/index", index)
// 局部中间件
r.GET("/home", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "home",
})
})
r.GET("/add", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "add",
})
})
r.Run(":9999")
}
四、Abort
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start := time.Now()
c.Next() // 调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
c.Abort() // 阻止调用后续处理的函数
return // 后面m2将不会在执行
fmt.Println("m2 out ....")
}
func main() {
r := gin.Default()
// 全局注册中间
r.Use(m1, m2)
r.GET("/index", index)
// 局部中间件
r.GET("/home", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "home",
})
})
r.GET("/add", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "add",
})
})
r.Run(":9999")
}
中间件return语句
五、模拟认证
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start := time.Now()
c.Next() // 调用后续处理的函数
//c.Abort() // 阻止调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
c.Next() // 调用后续处理的函数
fmt.Println("m2 out ....")
}
// 认证中间件
//func authMiddleware(c *gin.Context) {
// // 是否登录判断
// username := c.Query("username")
// // 判断是否登录用户
// if username == "RandySun" {
//
// c.Next()
// } else {
// // 认证失败
// c.JSON(http.StatusUnauthorized, gin.H{
// "msg": "没有权限",
// })
// c.Abort()
// }
//}
// 通过闭包认证中间件
func authMiddleware(doCheck bool) gin.HandlerFunc {
// 连接数据库
// 或者其他准备工作
return func(c *gin.Context) {
// 是否登录判断
username := c.Query("username")
// 判断是否登录用户
if doCheck {
if username == "RandySun" && doCheck {
c.Next()
} else {
// 认证失败
c.JSON(http.StatusUnauthorized, gin.H{
"msg": "没有权限",
})
c.Abort()
}
} else {
// 放行认证
c.Next()
}
}
}
func main() {
r := gin.Default()
// 全局注册中间
r.Use(m1, m2, authMiddleware(true))
r.GET("/index", index)
r.Run(":9999")
}
六、为路由组注册中间件
为路由组注册中间件有以下两种写法。
写法1:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 通过闭包认证中间件
func authMiddleware(doCheck bool) gin.HandlerFunc {
// 连接数据库
// 或者其他准备工作
return func(c *gin.Context) {
// 是否登录判断
username := c.Query("username")
// 判断是否登录用户
if doCheck {
if username == "RandySun" && doCheck {
c.Next()
} else {
// 认证失败
c.JSON(http.StatusUnauthorized, gin.H{
"msg": "没有权限",
})
c.Abort()
}
} else {
// 放行认证
c.Next()
}
}
}
func main() {
r := gin.Default()
// 为路由组添加中间件
shopGroup := r.Group("/shop", authMiddleware(true))
{
shopGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"mgs": "shop index",
})
})
}
r.Run(":9999")
}
写法2:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 通过闭包认证中间件
func authMiddleware(doCheck bool) gin.HandlerFunc {
// 连接数据库
// 或者其他准备工作
return func(c *gin.Context) {
// 是否登录判断
username := c.Query("username")
// 判断是否登录用户
if doCheck {
if username == "RandySun" && doCheck {
c.Next()
} else {
// 认证失败
c.JSON(http.StatusUnauthorized, gin.H{
"msg": "没有权限",
})
c.Abort()
}
} else {
// 放行认证
c.Next()
}
}
}
func main() {
r := gin.Default()
shopGroup := r.Group("/shop")
// 为路由组添加中间件
shopGroup.Use(authMiddleware(true))
{
shopGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"mgs": "shop index",
})
})
}
r.Run(":9999")
}
七、中间件取值
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func index(c *gin.Context) {
fmt.Println("index")
name, _ := c.Get("name")
// 跨中间件取值
fmt.Println("获取在中间中设置的值name:", name)
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
// 计算请求时间
start := time.Now()
c.Next() // 调用后续处理的函数
//c.Abort() // 阻止调用后续处理的函数
end := time.Since(start)
fmt.Printf("消耗时间time:%v\n", end)
fmt.Println("m1 out ....")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
// 在中间中设置值
c.Set("name", "randySun")
c.Next() // 调用后续处理的函数
fmt.Println("m2 out ....")
}
func main() {
r := gin.Default()
// 全局注册中间
r.Use(m1, m2)
r.GET("/index", index)
r.Run(":9999")
}
八、中间件
在gin框架中,我们可以为每个路由添加任意数量的中间件。
为全局路由注册
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!",
})
})
中间件注意事项
gin默认中间件
gin.Default()
默认使用了Logger
和Recovery
中间件,其中:
Logger
中间件将日志写入gin.DefaultWriter
,即使配置了GIN_MODE=release
。Recovery
中间件会recover任何panic
。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.New()
新建一个没有任何默认中间件的路由。
gin中间件中使用goroutine
当在中间件或handler
中启动新的goroutine
时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy()
)。
九、运行多个服务
多个端口启动服务,例如:
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
var (
g errgroup.Group
)
func router01() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 01",
},
)
})
return e
}
func router02() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 02",
},
)
})
return e
}
func main() {
server01 := &http.Server{
Addr: ":8080",
Handler: router01(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":8081",
Handler: router02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
// 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务
g.Go(func() error {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}