Golang Gin中間件的使用

在 Gin 框架中,如果多個(gè)路由有相同的處理邏輯,可以將該處理邏輯封裝成一個(gè)中間件函數(shù),然后將該中間件函數(shù)應(yīng)用到需要共享該處理邏輯的路由上。這樣,就可以避免代碼冗余,提高代碼復(fù)用性。

中間件函數(shù)是一個(gè)函數(shù),它接收一個(gè) *gin.Context 參數(shù),處理完該參數(shù)后,再調(diào)用 c.Next() 將請(qǐng)求交給下一個(gè)處理函數(shù)繼續(xù)處理。中間件函數(shù)通常用于實(shí)現(xiàn)一些公共的功能,比如登錄驗(yàn)證、身份認(rèn)證、日志記錄等。

下面是一個(gè)示例代碼,演示了如何在 Gin 框架中使用中間件函數(shù)共享相同的處理邏輯:

package main

import (
    "fmt"
    "net/http"

    "github.com/gin-gonic/gin"
)

// 中間件函數(shù)
func loggingMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("loggingMiddleware start")
        c.Next()
        fmt.Println("loggingMiddleware end")
    }
}

func main() {
    r := gin.Default()

    // 將中間件函數(shù)應(yīng)用到需要共享處理邏輯的路由上
    r.Use(loggingMiddleware())

    r.GET("/user/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(http.StatusOK, gin.H{"user_id": id})
    })

    r.POST("/user", func(c *gin.Context) {
        var user struct {
            Name  string `json:"name"`
            Email string `json:"email"`
        }

        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, gin.H{"name": user.Name, "email": user.Email})
    })

    r.Run(":8080")
}

在上面的代碼中,定義了一個(gè)名為 loggingMiddleware 的中間件函數(shù),它會(huì)在處理路由之前和之后分別輸出日志。然后,通過 r.Use(loggingMiddleware()) 將該中間件函數(shù)應(yīng)用到所有路由上。這樣,無論客戶端請(qǐng)求的是 /user/:id 路由還是 /user 路由,都會(huì)先執(zhí)行中間件函數(shù),再執(zhí)行路由處理函數(shù)。

在Gin框架中,通過使用Use()方法注冊(cè)中間件。Use()方法接受一個(gè)或多個(gè)函數(shù)作為參數(shù),并將它們添加到中間件棧中。這些函數(shù)將在每個(gè)路由處理程序被調(diào)用之前按注冊(cè)順序執(zhí)行。

需要注意的是,如果需要將中間件函數(shù)應(yīng)用到指定的路由上,可以使用 r.GET("/user/:id", loggingMiddleware(), func(c *gin.Context) {...}) 的形式,將中間件函數(shù)作為參數(shù)傳遞給路由處理函數(shù)即可,例如。

r.GET("/", loggingMiddleware(), func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"})
})

通過在路由處理器函數(shù)調(diào)用之前添加loggingMiddleware()函數(shù),我們將中間件與該路由處理程序綁定。

還可以使用Group()方法將一組路由處理器分組,并將一組中間件應(yīng)用于該組。

    // 作用于某個(gè)組
    authorizedApi := r.Group("/api")
    authorizedApi.Use(AuthMiddleware())
    {
        authorizedApi.POST("/first", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{
                "message": "first",
            })
        })
        authorizedApi.POST("/second", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{
                "message": "second",
            })
        })
    }

另一個(gè)例子

Gin中間件還可以通過傳遞參數(shù)進(jìn)行自定義。例如,我們可以將需要跟蹤的路徑作為參數(shù)傳遞給中間件。下面是一個(gè)示例:

func Logger(trackPath string) gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.URL.Path == trackPath {
            log.Println("Tracking path: ", trackPath)
        }

        c.Next()
    }
}

func main() {
    r := gin.Default()

    // 注冊(cè)中間件
    r.Use(Logger("/login"))

    r.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"})
    })

    r.Run(":8080")
}

在這個(gè)示例中,我們修改了Logger()函數(shù),以接受一個(gè)trackPath參數(shù)。在函數(shù)內(nèi)部,我們檢查請(qǐng)求的URL路徑是否等于trackPath。如果是,我們輸出一條記錄消息。在main函數(shù)中,我們使用Use()方法將Logger()函數(shù)注冊(cè)為中間件,并傳遞"/login"作為跟蹤路徑。

錯(cuò)誤處理的例子

除了在請(qǐng)求處理之前或之后執(zhí)行某些操作外,中間件還可以用于處理錯(cuò)誤。Gin框架內(nèi)置了一些錯(cuò)誤處理中間件,例如gin.Recovery()gin.CustomRecovery()。這些中間件可以用于捕獲和處理應(yīng)用程序中的錯(cuò)誤。在默認(rèn)情況下,Gin會(huì)使用gin.Recovery()中間件捕獲所有未處理的panic,并返回一個(gè)HTTP 500錯(cuò)誤。

除此之外,我們還可以自定義錯(cuò)誤處理中間件。以下是一個(gè)示例:

func ErrorHandler() gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if r := recover(); r != nil {
                c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
                    "error": "Internal server error",
                })
            }
        }()

        c.Next()
    }
}

func main() {
    r := gin.Default()

    // 注冊(cè)自定義錯(cuò)誤處理中間件
    r.Use(ErrorHandler())

    r.GET("/", func(c *gin.Context) {
        // 模擬一個(gè)panic
        panic("test error")
    })

    r.Run(":8080")
}

在這個(gè)示例中,我們創(chuàng)建了一個(gè)名為ErrorHandler()的中間件。在該中間件中,我們使用defer語句來捕獲所有未處理的panic,并返回一個(gè)HTTP 500錯(cuò)誤。在main函數(shù)中,我們使用Use()方法將ErrorHandler()函數(shù)注冊(cè)為中間件。在"/"路由處理器中,我們使用panic()函數(shù)來模擬一個(gè)錯(cuò)誤。

在上面的示例中,我們可以看到如何自定義一個(gè)錯(cuò)誤處理中間件,并捕獲未處理的panic。使用這種方法,我們可以更好地控制應(yīng)用程序的錯(cuò)誤處理方式,并提高應(yīng)用程序的可靠性。

身份驗(yàn)證的例子

另一個(gè)常見的中間件是身份驗(yàn)證中間件。它可以用于保護(hù)需要授權(quán)訪問的API端點(diǎn)。下面是一個(gè)示例:

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.Request.Header.Get("Authorization")
        if token == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "Unauthorized",
            })
            return
        }

        // TODO: 驗(yàn)證token

        c.Next()
    }
}

func main() {
    r := gin.Default()

    // 注冊(cè)身份驗(yàn)證中間件
    r.Use(AuthMiddleware())

    r.GET("/protected", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "Hello, World!",
        })
    })
   

    r.Run(":8080")
}

在上面的示例中,我們定義了一個(gè)名為AuthMiddleware()的中間件。該中間件檢查請(qǐng)求標(biāo)頭中是否存在Authorization標(biāo)頭。如果不存在,則返回HTTP 401未經(jīng)授權(quán)的錯(cuò)誤響應(yīng)。如果存在,則驗(yàn)證token,并調(diào)用Next()方法,將控制權(quán)移交給下一個(gè)中間件或路由處理器。在main函數(shù)中,我們使用Use()方法將AuthMiddleware()函數(shù)注冊(cè)為中間件。在"/protected"路由處理器中,我們只允許已經(jīng)通過身份驗(yàn)證的用戶訪問。

這是一個(gè)基本的身份驗(yàn)證中間件,它只檢查請(qǐng)求標(biāo)頭中是否存在Authorization標(biāo)頭。在實(shí)際應(yīng)用中,我們需要根據(jù)具體情況進(jìn)行修改和擴(kuò)展,例如驗(yàn)證token是否有效、判斷用戶是否有權(quán)限訪問API等。

除了以上介紹的中間件之外,Gin框架還提供了許多其他內(nèi)置中間件,如gin.Logger()、gin.Static()、gin.CORS()等。此外,你還可以編寫自己的中間件來實(shí)現(xiàn)您需要的功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容