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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", cost)
fmt.Println("m1 out")
}