Golang Gin框架中間件的用法詳解
一、中間件的基本概念
中間件是一個函數(shù),它在HTTP請求處理的生命周期中的某個特定點被調(diào)用,可以對請求和響應(yīng)進(jìn)行預(yù)處理或后處理。中間件的主要功能包括日志記錄、身份驗證、權(quán)限控制、跨域資源共享(CORS)、參數(shù)處理、錯誤處理等。
日志記錄:記錄請求的詳細(xì)信息,如請求路徑、請求體等。
身份驗證:驗證用戶的身份,如檢查令牌、Cookie等。
權(quán)限控制:確定用戶是否有權(quán)訪問特定的資源或執(zhí)行特定的操作。
跨域資源共享(CORS):允許不同域之間的資源共享。
參數(shù)處理:提取URL中的參數(shù)或請求體中的數(shù)據(jù)。
錯誤處理:捕獲并處理請求處理過程中的錯誤。
在Gin框架中,中間件必須是一個gin.HandlerFunc類型的函數(shù)。中間件函數(shù)接受一個*gin.Context參數(shù),并可以選擇返回一個gin.HandlerFunc。中間件函數(shù)的典型結(jié)構(gòu)如下:
func myHandler() gin.HandlerFunc {
return func(c *gin.Context) {
// 可以通過c.Set在請求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
c.Set("userSession", "userid-1")
//c.Next() // 放行,默認(rèn)就會放行
c.Abort() // 攔截,到這里就不會往下執(zhí)行請求了
fmt.Println("HandlerFunc-info")
}
}
二、中間件的兩個專屬方法
1. ctx.Next() 繼續(xù)
在程序進(jìn)入中間件的時候需要先進(jìn)行一些處理,然后去 執(zhí)行核心業(yè)務(wù),在執(zhí)行完核心業(yè)務(wù)之后再回來執(zhí)行該中間件。
2. ctx.Abort() 中斷
在程序進(jìn)入中間件之后我們進(jìn)行了一些操作,判斷該用戶不滿足訪問這個請求的條件,這個時候我們就需要終止這個請求,不讓其繼續(xù)執(zhí)行,這個時候就使用到了Abort
三、注冊中間件
在Gin框架中,可以通過全局注冊或單個路由中注冊,路由組注冊的方式來使用中間件。
1、定義一個我們自己的 HandlerFunc 如上
2、注冊全局路由
所有的請求都會經(jīng)過這里來處理
全局中間件會被應(yīng)用到所有的路由上。使用r.Use()函數(shù)來注冊全局中間件。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
// 定義一個中間件函數(shù)
func myHandler() gin.HandlerFunc {
//返回一個gin.HandlerFunc函數(shù)
return func(c *gin.Context) {
// 可以通過c.Set在請求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
// func (c *Context) Set(key string, value any)
c.Set("userSession", "userid-1")
//c.Next() // 放行,默認(rèn)就會放行
//c.Abort() // 攔截,到這里就不會往下執(zhí)行請求了 可以通過Abort做判定,控制請求的走向
fmt.Println("HandlerFunc-info")
}
}
func main() {
ginServer := gin.Default()
// 注冊 全局的 HandlerFunc
// func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
// 注意,這里面的參數(shù)是我們定義的中間件函數(shù)的執(zhí)行
ginServer.Use(myHandler())
// 接收請求
ginServer.GET("/test", func(c *gin.Context) {
// 從上下文取值 拿到我們在中間件中設(shè)置的值
name := c.MustGet("userSession").(string)
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"myname": name,
})
})
//再加個post請求,驗證全局中間件
ginServer.POST("/test", func(c *gin.Context) {
namePost := c.MustGet("userSession").(string)
log.Println(namePost)
c.JSON(http.StatusOK, gin.H{
"myname": namePost,
})
})
err := ginServer.Run()
if err != nil {
return
}
}
get請求拿到數(shù)據(jù)

post請求也拿到數(shù)據(jù)

3、為某個路由單獨注冊
為單個路由注冊的中間件,只能在該路由中生效
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
// 定義一個中間件函數(shù)
func myHandler() gin.HandlerFunc {
//返回一個gin.HandlerFunc函數(shù)
return func(c *gin.Context) {
// 可以通過c.Set在請求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
// func (c *Context) Set(key string, value any)
c.Set("userSession", "userid-1")
//c.Next() // 放行,默認(rèn)就會放行
//c.Abort() // 攔截,到這里就不會往下執(zhí)行請求了 可以通過Abort做判定,控制請求的走向
fmt.Println("HandlerFunc-info")
}
}
func main() {
ginServer := gin.Default()
// 單路由注冊中間件 ,只能在該路由中生效
ginServer.GET("/test", myHandler(), func(c *gin.Context) {
// 從上下文取值 拿到我們在中間件中設(shè)置的值
name := c.MustGet("userSession").(string)
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"myname": name,
})
})
//再加個post請求,post請求沒有注冊中間件,中間件在該路由中不生效
ginServer.POST("/test", func(c *gin.Context) {
//當(dāng)沒有在該路由中注冊中間件時,獲取中間件中的變量值,獲取不到會報panic
namePost := c.MustGet("userSession").(string)
log.Println(namePost)
c.JSON(http.StatusOK, gin.H{
"myname": namePost,
})
})
err := ginServer.Run()
if err != nil {
return
}
}

當(dāng)沒有在該路由中注冊中間件時,獲取中間件中的變量值,獲取不到會報panic

拿不到值報異常

4、為路由組注冊中間件
路由組中間件只會被應(yīng)用到該路由組下的路由上。通過創(chuàng)建一個路由組,并使用v1.Use()函數(shù)為該路由組注冊中間件。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
// 定義一個中間件函數(shù)
func myHandler() gin.HandlerFunc {
//返回一個gin.HandlerFunc函數(shù)
return func(c *gin.Context) {
// 可以通過c.Set在請求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
// func (c *Context) Set(key string, value any)
c.Set("userSession", "userid-1")
//c.Next() // 放行,默認(rèn)就會放行
//c.Abort() // 攔截,到這里就不會往下執(zhí)行請求了 可以通過Abort做判定,控制請求的走向
fmt.Println("HandlerFunc-info")
}
}
func main() {
ginServer := gin.Default()
//創(chuàng)建路由組
v1 := ginServer.Group("/v1") // 創(chuàng)建一個名為/v1的路由組
//路由組應(yīng)用中間件
v1.Use(myHandler())
// 路由組中間件
{
v1.GET("/test", func(c *gin.Context) {
// 從上下文取值 拿到我們在中間件中設(shè)置的值
name := c.MustGet("userSession").(string)
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"myname": name,
})
})
//再加個post請求,post請求沒有注冊中間件,中間件在該路由中不生效
v1.POST("/test", func(c *gin.Context) {
//當(dāng)沒有在該路由中注冊中間件時,獲取中間件中的變量值,獲取不到會報panic
namePost := c.MustGet("userSession").(string)
log.Println(namePost)
c.JSON(http.StatusOK, gin.H{
"myname": namePost,
})
})
}
err := ginServer.Run()
if err != nil {
return
}
}
在上述代碼中,我們創(chuàng)建了一個名為/v1的路由組,并為這個路由組注冊了一個中間件。這個中間件只會被應(yīng)用到/v1下的路由上。
路由組中間件也可以直接應(yīng)用在Group方法中
v1 := r.Group("/test", myHandler())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
5、gin默認(rèn)中間件
gin.Default()默認(rèn)使用了Logger和Recovery中間件,其中:
Logger中間件將日志寫入gin.DefaultWriter,即使配置了GIN_MODE=release。Recovery中間件會recover任何panic。如果有panic的話,會寫入500響應(yīng)碼。
如果不想使用上面兩個默認(rèn)的中間件,可以使用gin.New()新建一個沒有任何默認(rèn)中間件的路由。
三、實際應(yīng)用案例
以下將通過幾個實際案例來演示Gin框架中間件的用法。
1. 日志記錄中間件
日志記錄中間件用于記錄請求的詳細(xì)信息,如請求路徑、請求體、響應(yīng)狀態(tài)碼等。
package middleware
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
// LoggerMiddleware 是一個日志記錄中間件
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 記錄請求開始時的信息
startTime := time.Now()
// 調(diào)用后續(xù)的處理函數(shù)
c.Next()
// 記錄請求結(jié)束時的信息
endTime := time.Now()
latency := endTime.Sub(startTime)
// 記錄請求的IP地址和端口號
ipAddress := c.ClientIP()
// 記錄請求的URL
requestUrl := c.Request.URL.Path
// 記錄請求的方法
httpMethod := c.Request.Method
// 記錄請求的狀態(tài)碼
statusCode := c.Writer.Status()
// 記錄日志信息
log.Printf("Request from %s to %s took %s with method %s and status code %d\n", ipAddress, requestUrl, latency, httpMethod, statusCode)
}
}
在main.go中使用該中間件:
package main
import (
"github.com/gin-gonic/gin"
"jingtian/jt_gin/middleware"
"net/http"
)
func main() {
r := gin.Default() // 創(chuàng)建一個默認(rèn)的Gin引擎
// 注冊中間件
r.Use(middleware.LoggerMiddleware())
// 定義路由
r.GET("/test", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"})
})
err := r.Run(":8080")
if err != nil {
return
} // 啟動服務(wù)器
}
請求

可以看到日志中間件打印出的日志

2. 身份驗證中間件
身份驗證中間件用于驗證用戶的身份,如檢查令牌、Cookie等。
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
// AuthMiddleware 是一個身份驗證中間件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 假設(shè)我們使用Bearer令牌進(jìn)行身份驗證
token := c.GetHeader("Authorization")
//查看token
fmt.Println("查看token:", token)
if token != "Bearer your_token_here" {
//認(rèn)證未通過,返回401狀態(tài)碼和錯誤消息
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized access"})
c.Abort()
return
}
// 調(diào)用后續(xù)的處理函數(shù)
c.Next()
}
}
在main.go中使用該中間件:
package main
import (
"github.com/gin-gonic/gin"
"jingtian/jt_gin/middleware"
"net/http"
)
func main() {
r := gin.Default() // 創(chuàng)建一個默認(rèn)的Gin引擎
// 注冊中間件
r.Use(middleware.AuthMiddleware())
// 定義路由
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Hello, Authenticated User!"})
})
r.Run(":8080") // 啟動服務(wù)器
}

3. 限流中間件
限流中間件用于限制特定時間段內(nèi)允許的請求數(shù)量,以防止服務(wù)器過載。
package main
import (
"github.com/gin-gonic/gin"
"time"
)
var (
limiter = NewLimiter(10, 1*time.Minute) // 設(shè)置限流器,允許每分鐘最多請求10次
)
// NewLimiter 創(chuàng)建限流器
func NewLimiter(limit int, duration time.Duration) *Limiter {
return &Limiter{
limit: limit,
duration: duration,
timestamps: make(map[string][]int64),
}
}
// Limiter 限流器
type Limiter struct {
limit int // 限制的請求數(shù)量
duration time.Duration // 時間窗口
timestamps map[string][]int64 // 請求的時間戳
}
// Middleware 限流中間件
func (l *Limiter) Middleware(c *gin.Context) {
ip := c.ClientIP() // 獲取客戶端IP地址
// 檢查請求時間戳切片是否存在
if _, ok := l.timestamps[ip]; !ok {
l.timestamps[ip] = make([]int64, 0)
}
now := time.Now().Unix() // 當(dāng)前時間戳
// 移除過期的請求時間戳
for i := 0; i < len(l.timestamps[ip]); i++ {
if l.timestamps[ip][i] < now-int64(l.duration.Seconds()) {
l.timestamps[ip] = append(l.timestamps[ip][:i], l.timestamps[ip][i+1:]...)
i--
}
}
// 檢查請求數(shù)量是否超過限制
if len(l.timestamps[ip]) >= l.limit {
c.JSON(429, gin.H{
"message": "Too Many Requests",
})
c.Abort()
return
}
// 添加當(dāng)前請求時間戳到切片
l.timestamps[ip] = append(l.timestamps[ip], now)
// 繼續(xù)處理請求
c.Next()
}
func main() {
r := gin.Default()
// 使用限流中間件
r.Use(limiter.Middleware)
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello World",
})
})
r.Run(":8080")
}
每分鐘內(nèi)訪問超過10次,就限流了

以上就是Golang Gin框架中間件的用法詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang Gin框架中間件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言使用kafka-go實現(xiàn)Kafka消費消息
本篇文章主要介紹了使用kafka-go庫消費Kafka消息,包含F(xiàn)etchMessage和ReadMessage的區(qū)別和適用場景,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的可以了解一下2024-12-12
Go語言中GORM存取數(shù)組/自定義類型數(shù)據(jù)
在使用gorm時往往默認(rèn)的數(shù)據(jù)類型不滿足我們的要求,需要使用一些自定義數(shù)據(jù)類型作為字段類型,下面這篇文章主要給大家介紹了關(guān)于Go語言中GORM存取數(shù)組/自定義類型數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2023-01-01
解決Golang小數(shù)float64在實際工程中加減乘除的精度問題
這篇文章主要介紹了解決Golang小數(shù)float64在實際工程中加減乘除的精度問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
在 Golang 中實現(xiàn)一個簡單的Http中間件過程詳解
本文在go web中簡單的實現(xiàn)了中間件的機制,這樣帶來的好處也是顯而易見的,當(dāng)然社區(qū)也有一些成熟的 middleware 組件,包括 Gin 一些Web框架中也包含了 middleware 相關(guān)的功能,具體內(nèi)容詳情跟隨小編一起看看吧2021-07-07
Ubuntu下安裝Go語言開發(fā)環(huán)境及編輯器的相關(guān)配置
這篇文章主要介紹了Ubuntu下安裝Go語言開發(fā)環(huán)境及編輯器的相關(guān)配置,編輯器方面介紹了包括Vim和Eclipse,需要的朋友可以參考下2016-02-02
Golang觀察者模式優(yōu)化訂單處理系統(tǒng)實例探究
當(dāng)涉及到訂單處理系統(tǒng)時,觀察者設(shè)計模式可以用于實現(xiàn)訂單狀態(tài)的變化和通知,在這篇文章中,我們將介紹如何使用Golang來實現(xiàn)觀察者設(shè)計模式,并提供一個基于訂單處理系統(tǒng)的代碼示例2024-01-01

