1 單獨(dú)注冊中間件
Gin框架允許開發(fā)者在處理請求的過程中,加入用戶自己的鉤子(Hook)函數(shù)。這個鉤子函數(shù)就叫中間件,中間件適合處理一些公共的業(yè)務(wù)邏輯,比如登錄認(rèn)證、權(quán)限校驗(yàn)、數(shù)據(jù)分頁、記錄日志、耗時(shí)統(tǒng)計(jì)等
即比如,如果訪問一個網(wǎng)頁的話,不管訪問什么路徑都需要進(jìn)行登錄,此時(shí)就需要為所有路徑的處理函數(shù)進(jìn)行統(tǒng)一一個中間件
Gin中的中間件必須是一個gin.HandlerFunc類型
1.1 入門案例
我們先看一下Get函數(shù)能夠接收的參數(shù):
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
type HandlerFunc func(*Context)
從這個函數(shù)里,我們能看到它可以,它可以接收很多的HandlerFunc類型的數(shù)據(jù),并且發(fā)現(xiàn)是func(*Context)數(shù)據(jù)類型都可以,所以,我們可以定義很多個中間件,它必須是*gin.Context
類型
- 定義一個中間件
func m1(c *gin.Context) {
fmt.Println("m1 in.........")
}
- 寫一個函數(shù),在前端響應(yīng)的數(shù)據(jù),作為參考
func indexHandler(c *gin.Context) {
fmt.Println("index.....")
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
}
- 完整代碼
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函數(shù)的前面,請求來之后,先走m1,再走index
r.GET("/index", m1, indexHandler)
r.Run(":8000")
}
注意:m1處于indexHandler函數(shù)的前面,請求來之后,先走m1,再走index
然后,使用游覽器訪問對應(yīng)的請求,看一下輸出:
驗(yàn)證完畢??!~
1.2 多個中間件
router.GET,后面可以跟很多HandlerFunc方法,這些方法其實(shí)都可以叫中間件
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": "響應(yīng)數(shù)據(jù)"})
}, m2)
router.Run(":8080")
}
這里就不演示了~~
1.3 中間件攔截響應(yīng)
c.Abort()函數(shù)
,作用:攔截,后續(xù)的HandlerFunc就不會執(zhí)行了
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": "響應(yīng)數(shù)據(jù)"})
}, m2)
router.Run(":8080")
}
運(yùn)行后,去游覽器試一下,會發(fā)現(xiàn)只輸出:m1 …in,也就是被第一個攔截器攔截了~
1.4 中間件放行
c.Next()函數(shù)
,作用:Next前后形成了其他語言中的請求中間件和響應(yīng)中間件
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": "響應(yīng)數(shù)據(jù)"})
c.Next()
fmt.Println("index ...out")
}, m2)
router.Run(":8080")
}
輸出結(jié)果:
m1 ...in
index ...in
m2 ...in
m2 ...out
index ...out
m1 ...out
輸出的方式,有點(diǎn)像棧,先進(jìn)去的m1...out
最后再輸出~
2 全局注冊中間件
在Gin框架中,可以使用Use
方法注冊全局中間件。全局中間件會對所有的請求都生效。
func main() {
r := gin.Default()
r.Use(Logger()) // 注冊全局中間件
r.GET("/hello", HelloHandler)
r.Run(":8080")
}
// Logger 是一個全局中間件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
end := time.Now()
latency := end.Sub(start)
log.Printf("[%s] %s %s %v", c.Request.Method, c.Request.URL.Path, c.Request.RemoteAddr, latency)
}
}
func HelloHandler(c *gin.Context) {
c.String(http.StatusOK, "Hello, Gin!")
}
在上面的示例中,Logger
函數(shù)是一個全局中間件。它在處理請求之前記錄了請求的相關(guān)信息,并在請求處理完成后打印了請求的耗時(shí)。
3 自定義參數(shù)傳遞
在中間件之間傳遞自定義參數(shù)是一種常見的需求。在Gin框架中,可以使用Context.Set
和Context.Get
方法來傳遞自定義參數(shù),并且傳遞的數(shù)據(jù)是一個key-value
func main() {
r := gin.Default()
r.Use(AddCustomData("custom data"))
r.GET("/hello", HelloHandler)
r.Run(":8080")
}
func AddCustomData(data string) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("customData", data) // 設(shè)置自定義參數(shù)
c.Next()
}
}
func HelloHandler(c *gin.Context) {
customData, exists := c.Get("customData") // 獲取自定義參數(shù)
if exists {
c.String(http.StatusOK, "Hello, Gin! Custom data: %s", customData)
} else {
c.String(http.StatusOK, "Hello, Gin!")
}
}
在上面的示例中,AddCustomData
函數(shù)返回了一個中間件函數(shù),用于在Context中設(shè)置自定義參數(shù)。在HelloHandler
處理函數(shù)中,通過Context.Get
方法獲取自定義參數(shù)并使用。
4 路由分組
4.1 入門案例
將一系列的路由放到一個組下,統(tǒng)一管理。例如,以下的路由前面統(tǒng)一加上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")
}
4.2 路由分組注冊中間件
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) // 可以鏈?zhǔn)?,也可以直接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")
}
這樣寫我們就可以指定哪一些分組下可以使用中間件了
當(dāng)然,中間件還有一種寫法,就是使用函數(shù)加括號的形式
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func middle(c *gin.Context) {
fmt.Println("middle ...in")
}
func middle1() gin.HandlerFunc {
// 這里的代碼是程序一開始就會執(zhí)行
return func(c *gin.Context) {
// 這里是請求來了才會執(zhí)行
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")
}
4.3 綜合使用
設(shè)置了一個中間件進(jìn)行身份驗(yàn)證,并且還對路由進(jìn)行分組,分為兩組來使用
func main() {
r := gin.Default()
r.Use(Logger()) // 注冊全局中間件
v1 := r.Group("/v1")
v1.Use(Auth()) // 注冊 v1 路由組的局部中間件
{
v1.GET("/hello", HelloHandler)
v1.GET("/user", UserHandler)
}
r.GET("/hello", HelloHandler) // 其他路由不使用 Auth 中間件
r.Run(":8080")
}
func Auth() gin.HandlerFunc {
return func(c *gin.Context) {
// 進(jìn)行身份驗(yàn)證的邏輯
if IsAuthenticated {
c.Next()
} else {
c.AbortWithStatus(http.StatusUnauthorized)
}
}
}
func HelloHandler(c *gin.Context) {
c.String(http.StatusOK, "Hello, Gin!")
}
func UserHandler(c *gin.Context) {
c.String(http.StatusOK, "User Info")
}
// Logger 是一個全局中間件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
end := time.Now()
latency := end.Sub(start)
log.Printf("[%s] %s %s %v", c.Request.Method, c.Request.URL.Path, c.Request.RemoteAddr, latency)
}
}
5 使用內(nèi)置的中間件
Gin框架內(nèi)置了一些常用的中間件,可以直接使用。以下是一些常用的內(nèi)置中間件和示例:
-
gin.Logger()
:記錄請求日志 -
gin.Recovery()
:處理請求時(shí)的恢復(fù)機(jī)制
func main() {
r := gin.Default()
r.Use(gin.Logger()) // 使用 gin.Logger() 中間件
r.Use(gin.Recovery()) // 使用 gin.Recovery() 中間件
r.GET("/hello", HelloHandler)
r.Run(":8000")
}
func HelloHandler(c *gin.Context) {
c.String(http.StatusOK, "Hello, Gin!")
}
不過,平常我們不使用這兩個中間件的時(shí)候,一樣會有記錄,所以我們看一下gin.Default函數(shù)
。
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
從這段代碼就可以看出,是默認(rèn)使用了這兩個函數(shù),一般自動調(diào)用了~~~
6 中間件案例
權(quán)限驗(yàn)證
以前后端最流行的jwt為例,如果用戶登錄了,前端發(fā)來的每一次請求都會在請求頭上攜帶上token
后臺拿到這個token進(jìn)行校驗(yàn),驗(yàn)證是否過期,是否非法
如果通過就說明這個用戶是登錄過的
不通過就說明用戶沒有登錄文章來源:http://www.zghlxwxcb.cn/news/detail-729240.html
package main
import (
"github.com/gin-gonic/gin"
)
func JwtTokenMiddleware(c *gin.Context) {
// 獲取請求頭的token
token := c.GetHeader("token")
// 調(diào)用jwt的驗(yàn)證函數(shù)
if token == "1234" {
// 驗(yàn)證通過
c.Next()
return
}
// 驗(yàn)證不通過
c.JSON(200, gin.H{"msg": "權(quán)限驗(yàn)證失敗"})
c.Abort()
}
func main() {
router := gin.Default()
api := router.Group("/api")
apiUser := api.Group("")
{
apiUser.POST("login", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "登錄成功"})
})
}
apiHome := api.Group("system").Use(JwtTokenMiddleware)
{
apiHome.GET("/index", func(c *gin.Context) {
c.String(200, "index")
})
apiHome.GET("/home", func(c *gin.Context) {
c.String(200, "home")
})
}
router.Run(":8080")
}
耗時(shí)統(tǒng)計(jì)
統(tǒng)計(jì)每一個視圖函數(shù)的執(zhí)行時(shí)間文章來源地址http://www.zghlxwxcb.cn/news/detail-729240.html
func TimeMiddleware(c *gin.Context) {
startTime := time.Now()
c.Next()
since := time.Since(startTime)
// 獲取當(dāng)前請求所對應(yīng)的函數(shù)
f := c.HandlerName()
fmt.Printf("函數(shù) %s 耗時(shí) %d\n", f, since)
}
到了這里,關(guān)于Go學(xué)習(xí)第十七章——Gin中間件與路由的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!