Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等
定义中间件
Gin中的中间件必须是一个gin.HandlerFunc类型
入门案例
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
c.JSON(http.StatusOK, gin.H{
"msg": "indx",
})
}
//定义一个中间件
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
}
func main() {
r := gin.Default()
//GET(relativePath string, handlers ...HandlerFunc) IRoutes
r.GET("/index",m1,indexHandler)
//r.GET("/index", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "msg": "indx",
// })
//})
r.Run(":9090")
}
[GIN-debug] Listening and serving HTTP on :9090
m1 in ....
index in ...
[GIN] 2020/04/21 - 15:21:31 |?[97;42m 200 ?[0m| 998.3µs | 127.0.0.1 |?[97;44m GET ?[0m "/index"
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
c.JSON(http.StatusOK, gin.H{
"msg": "indx",
})
}
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
//输出
// m1 in ....
//index in ...
//cost:%v
// 996.8µs
}
func main() {
r := gin.Default()
//GET(relativePath string, handlers ...HandlerFunc) IRoutes
r.GET("/index",m1,indexHandler) //先执行m1函数再执行indexHandler函数
//r.GET("/index", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "msg": "indx",
// })
//})
r.Run(":9090")
}
注册中间件
在gin框架中,可以为每个路由添加任意数量的中间件。
为全局路由注册
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
c.JSON(http.StatusOK, gin.H{
"msg": "indx",
})
}
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
//输出
// [GIN-debug] Listening and serving HTTP on :9090
//m1 in ....
//m2 in ....
//index in ...
//m2 out
//cost:%v
// 997.3µs
//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) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用
//GET(relativePath string, handlers ...HandlerFunc) IRoutes
//r.GET("/index",m1,indexHandler) //先执行m1函数再执行indexHandler函数
r.GET("/index",indexHandler)
r.GET("/shop", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.GET("/user", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.Run(":9090")
}
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
c.JSON(http.StatusOK, gin.H{
"msg": "indx",
})
}
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
//输出
// [GIN-debug] Listening and serving HTTP on :9090
//m1 in ....
//m2 in ....
//m2 out
//cost:%v
// 997.8µs
//m1 out
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
//c.Next() //调用后续的处理函数
c.Abort() //阻止后续调用
//return //return 立即结束m2函数
//m1 in ....
//m2 in ....
//cost:%v
// 0s
//m1 out
fmt.Println("m2 out")
}
//func authMiddleware(c *gin.Context) { //通常写成闭包
// //是否登陆的判断
// //if 是登陆用户
// //c.Next()
// //else
// //c.Abort()
//}
func authMiddleware(doCheck bool)gin.HandlerFunc { //开关注册
//连接数据库
//或着其他准备工作
return func(c *gin.Context) {
if doCheck {
//是否登陆的判断
//if 是登陆用户
//c.Next()
//else
//c.Abort()
} else {
c.Next()
}
}
}
func main() {
r := gin.Default()
r.Use(m1,m2,authMiddleware(true)) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用
//GET(relativePath string, handlers ...HandlerFunc) IRoutes
//r.GET("/index",m1,indexHandler) //先执行m1函数再执行indexHandler函数
r.GET("/index",indexHandler)
r.GET("/shop", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.GET("/user", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.Run(":9090")
}
为某个路由单独注册
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
}
func main() {
r := gin.Default()
r.GET("/user", m1, func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
//m1 in ....
//cost:%v
// 0s
//m1 out
r.Run(":9090")
}
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
c.JSON(http.StatusOK, gin.H{
"msg": "indx",
})
}
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
//c.Next() //调用后续的处理函数
c.Abort() //阻止后续调用
//return //return 立即结束m2函数
//m1 in ....
//m2 in ....
//cost:%v
// 0s
//m1 out
fmt.Println("m2 out")
}
func main() {
r := gin.Default()
r.GET("/user", m1,m2, func(c *gin.Context) { //可以单独多个路由
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
//[GIN-debug] Listening and serving HTTP on :9090
//m1 in ....
//m2 in ....
//m2 out
//cost:%v
// 0s
//m1 out
r.Run(":9090")
}
为路由组注册中间件
func main() {
//路由组注册中间件方法1:
xxGroup := r.Group("/xx", authMiddleware(true))
{
xxGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg":"xxGroup"})
})
}
//路由组注册中间件方法2:
xx2Group := r.Group("/xx")
xx2Group.Use(authMiddleware(true))
{
xxGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg":"xxGroup"})
})
}
r.Run(":9090")
}
跨中间件存取值
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
name, ok := c.Get("name") //从上下文中取值,跨中间件存取值
if !ok {
name = "匿名用户"
}
c.JSON(http.StatusOK, gin.H{
"msg": name,
})
}
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
c.Set("name","zisefeizhu") //在上下文中设置c的值
fmt.Println("m2 out")
}
func authMiddleware(doCheck bool)gin.HandlerFunc { //开关注册
//连接数据库
//或着其他准备工作
return func(c *gin.Context) {
if doCheck {
//是否登陆的判断
//if 是登陆用户
c.Next()
//else
//c.Abort()
} else {
c.Next()
}
}
}
func main() {
r := gin.Default()
r.Use(m1,m2,authMiddleware(true)) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用
//GET(relativePath string, handlers ...HandlerFunc) IRoutes
//r.GET("/index",m1,indexHandler) //先执行m1函数再执行indexHandler函数
r.GET("/index",indexHandler)
r.Run(":9090")
}
中间件注意事项
gin.Default()
gin.Default()默认使用了Logger和Recovery中间件,其中:Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
fmt.Println("index in ...")
name, ok := c.Get("name") //从上下文中取值,跨中间件存取值
if !ok {
name = "匿名用户"
}
c.JSON(http.StatusOK, gin.H{
"msg": name,
})
}
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
}
func m2(c *gin.Context) {
fmt.Println("m2 in ....")
c.Set("name","zisefeizhu") //在上下文中设置c的值
fmt.Println("m2 out")
}
func authMiddleware(doCheck bool)gin.HandlerFunc { //开关注册
//连接数据库
//或着其他准备工作
return func(c *gin.Context) {
if doCheck {
//是否登陆的判断
//if 是登陆用户
c.Next()
//else
//c.Abort()
} else {
c.Next()
}
}
}
func main() {
//r := gin.Default() //默认使用Logger()和Recovery()中间件
r := gin.New()
r.Use(m1,m2,authMiddleware(true)) //全局注册中间件函数m1,m2 洋葱模型 类似递归调用
r.GET("/index",indexHandler)
r.GET("/shop", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.GET("/user", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
})
r.Run(":9090")
}
[GIN-debug] Listening and serving HTTP on :9090
m1 in ....
m2 in ....
m2 out
index in ...
cost:%v
1.0137ms
m1 out
gin中间件中使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。
//定义一个中间件:统计耗时
func m1(c *gin.Context) {
fmt.Println("m1 in ....")
//计时
start := time.Now()
go funcXX(c.Copy()) //在funcXX中只能使用c的拷贝
c.Next() //调用后续的处理函数 执行indexHandler函数
//c.Abort() //阻止调用后续的处理函数
cost := time.Since(start)
fmt.Println("cost:%v
", cost)
fmt.Println("m1 out")
}