欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang Gin框架中間件的用法詳解

 更新時(shí)間:2024年10月22日 09:06:24   作者:景天科技苑  
中間件是Gin框架中的一個(gè)核心概念,它允許開發(fā)者在處理HTTP請(qǐng)求的過(guò)程中插入自定義的鉤子函數(shù),從而實(shí)現(xiàn)諸如日志記錄、身份驗(yàn)證、權(quán)限控制等公共邏輯,本文將結(jié)合實(shí)際案例,詳細(xì)講解Gin框架中間件的用法

一、中間件的基本概念

中間件是一個(gè)函數(shù),它在HTTP請(qǐng)求處理的生命周期中的某個(gè)特定點(diǎn)被調(diào)用,可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理或后處理。中間件的主要功能包括日志記錄、身份驗(yàn)證、權(quán)限控制、跨域資源共享(CORS)、參數(shù)處理、錯(cuò)誤處理等。

日志記錄:記錄請(qǐng)求的詳細(xì)信息,如請(qǐng)求路徑、請(qǐng)求體等。
身份驗(yàn)證:驗(yàn)證用戶的身份,如檢查令牌、Cookie等。
權(quán)限控制:確定用戶是否有權(quán)訪問(wèn)特定的資源或執(zhí)行特定的操作。
跨域資源共享(CORS):允許不同域之間的資源共享。
參數(shù)處理:提取URL中的參數(shù)或請(qǐng)求體中的數(shù)據(jù)。
錯(cuò)誤處理:捕獲并處理請(qǐng)求處理過(guò)程中的錯(cuò)誤。

在Gin框架中,中間件必須是一個(gè)gin.HandlerFunc類型的函數(shù)。中間件函數(shù)接受一個(gè)*gin.Context參數(shù),并可以選擇返回一個(gè)gin.HandlerFunc。中間件函數(shù)的典型結(jié)構(gòu)如下:

func myHandler() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 可以通過(guò)c.Set在請(qǐng)求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
        c.Set("userSession", "userid-1")
        //c.Next() // 放行,默認(rèn)就會(huì)放行
        c.Abort() // 攔截,到這里就不會(huì)往下執(zhí)行請(qǐng)求了
        fmt.Println("HandlerFunc-info")
    }
}

二、中間件的兩個(gè)專屬方法

1. ctx.Next() 繼續(xù)

在程序進(jìn)入中間件的時(shí)候需要先進(jìn)行一些處理,然后去 執(zhí)行核心業(yè)務(wù),在執(zhí)行完核心業(yè)務(wù)之后再回來(lái)執(zhí)行該中間件。

2. ctx.Abort() 中斷

在程序進(jìn)入中間件之后我們進(jìn)行了一些操作,判斷該用戶不滿足訪問(wèn)這個(gè)請(qǐng)求的條件,這個(gè)時(shí)候我們就需要終止這個(gè)請(qǐng)求,不讓其繼續(xù)執(zhí)行,這個(gè)時(shí)候就使用到了Abort

三、注冊(cè)中間件

在Gin框架中,可以通過(guò)全局注冊(cè)或單個(gè)路由中注冊(cè),路由組注冊(cè)的方式來(lái)使用中間件。

1、定義一個(gè)我們自己的 HandlerFunc 如上

2、注冊(cè)全局路由

所有的請(qǐng)求都會(huì)經(jīng)過(guò)這里來(lái)處理
全局中間件會(huì)被應(yīng)用到所有的路由上。使用r.Use()函數(shù)來(lái)注冊(cè)全局中間件。

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)

// 定義一個(gè)中間件函數(shù)
func myHandler() gin.HandlerFunc {
    //返回一個(gè)gin.HandlerFunc函數(shù)
    return func(c *gin.Context) {
        // 可以通過(guò)c.Set在請(qǐng)求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
        // func (c *Context) Set(key string, value any)
        c.Set("userSession", "userid-1")
        //c.Next()  // 放行,默認(rèn)就會(huì)放行
        //c.Abort() // 攔截,到這里就不會(huì)往下執(zhí)行請(qǐng)求了  可以通過(guò)Abort做判定,控制請(qǐng)求的走向
        fmt.Println("HandlerFunc-info")
    }
}

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

    // 注冊(cè) 全局的 HandlerFunc
    // func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
    // 注意,這里面的參數(shù)是我們定義的中間件函數(shù)的執(zhí)行
    ginServer.Use(myHandler())

    // 接收請(qǐng)求
    ginServer.GET("/test", func(c *gin.Context) {
        // 從上下文取值 拿到我們?cè)谥虚g件中設(shè)置的值
        name := c.MustGet("userSession").(string)
        log.Println(name)
        c.JSON(http.StatusOK, gin.H{
            "myname": name,
        })
    })

    //再加個(gè)post請(qǐng)求,驗(yàn)證全局中間件
    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請(qǐng)求拿到數(shù)據(jù)

在這里插入圖片描述

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

在這里插入圖片描述

3、為某個(gè)路由單獨(dú)注冊(cè)

為單個(gè)路由注冊(cè)的中間件,只能在該路由中生效

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)

// 定義一個(gè)中間件函數(shù)
func myHandler() gin.HandlerFunc {
    //返回一個(gè)gin.HandlerFunc函數(shù)
    return func(c *gin.Context) {
        // 可以通過(guò)c.Set在請(qǐng)求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
        // func (c *Context) Set(key string, value any)
        c.Set("userSession", "userid-1")
        //c.Next()  // 放行,默認(rèn)就會(huì)放行
        //c.Abort() // 攔截,到這里就不會(huì)往下執(zhí)行請(qǐng)求了  可以通過(guò)Abort做判定,控制請(qǐng)求的走向
        fmt.Println("HandlerFunc-info")
    }
}

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

    // 單路由注冊(cè)中間件 ,只能在該路由中生效
    ginServer.GET("/test", myHandler(), func(c *gin.Context) {
        // 從上下文取值 拿到我們?cè)谥虚g件中設(shè)置的值
        name := c.MustGet("userSession").(string)
        log.Println(name)
        c.JSON(http.StatusOK, gin.H{
            "myname": name,
        })
    })

    //再加個(gè)post請(qǐng)求,post請(qǐng)求沒(méi)有注冊(cè)中間件,中間件在該路由中不生效
    ginServer.POST("/test", func(c *gin.Context) {
        //當(dāng)沒(méi)有在該路由中注冊(cè)中間件時(shí),獲取中間件中的變量值,獲取不到會(huì)報(bào)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)沒(méi)有在該路由中注冊(cè)中間件時(shí),獲取中間件中的變量值,獲取不到會(huì)報(bào)panic

在這里插入圖片描述

拿不到值報(bào)異常

在這里插入圖片描述

4、為路由組注冊(cè)中間件

路由組中間件只會(huì)被應(yīng)用到該路由組下的路由上。通過(guò)創(chuàng)建一個(gè)路由組,并使用v1.Use()函數(shù)為該路由組注冊(cè)中間件。

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)

// 定義一個(gè)中間件函數(shù)
func myHandler() gin.HandlerFunc {
    //返回一個(gè)gin.HandlerFunc函數(shù)
    return func(c *gin.Context) {
        // 可以通過(guò)c.Set在請(qǐng)求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
        // func (c *Context) Set(key string, value any)
        c.Set("userSession", "userid-1")
        //c.Next()  // 放行,默認(rèn)就會(huì)放行
        //c.Abort() // 攔截,到這里就不會(huì)往下執(zhí)行請(qǐng)求了  可以通過(guò)Abort做判定,控制請(qǐng)求的走向
        fmt.Println("HandlerFunc-info")
    }
}

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

    //創(chuàng)建路由組
    v1 := ginServer.Group("/v1") // 創(chuàng)建一個(gè)名為/v1的路由組
    //路由組應(yīng)用中間件
    v1.Use(myHandler())

    // 路由組中間件
    {
        v1.GET("/test", func(c *gin.Context) {
            // 從上下文取值 拿到我們?cè)谥虚g件中設(shè)置的值
            name := c.MustGet("userSession").(string)
            log.Println(name)
            c.JSON(http.StatusOK, gin.H{
                "myname": name,
            })
        })

        //再加個(gè)post請(qǐng)求,post請(qǐng)求沒(méi)有注冊(cè)中間件,中間件在該路由中不生效
        v1.POST("/test", func(c *gin.Context) {
            //當(dāng)沒(méi)有在該路由中注冊(cè)中間件時(shí),獲取中間件中的變量值,獲取不到會(huì)報(bào)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)建了一個(gè)名為/v1的路由組,并為這個(gè)路由組注冊(cè)了一個(gè)中間件。這個(gè)中間件只會(huì)被應(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)使用了LoggerRecovery中間件,其中:

  • Logger中間件將日志寫入gin.DefaultWriter,即使配置了GIN_MODE=release。
  • Recovery中間件會(huì)recover任何panic。如果有panic的話,會(huì)寫入500響應(yīng)碼。

如果不想使用上面兩個(gè)默認(rèn)的中間件,可以使用gin.New()新建一個(gè)沒(méi)有任何默認(rèn)中間件的路由。

三、實(shí)際應(yīng)用案例

以下將通過(guò)幾個(gè)實(shí)際案例來(lái)演示Gin框架中間件的用法。

1. 日志記錄中間件

日志記錄中間件用于記錄請(qǐng)求的詳細(xì)信息,如請(qǐng)求路徑、請(qǐng)求體、響應(yīng)狀態(tài)碼等。

package middleware

import (
    "github.com/gin-gonic/gin"
    "log"
    "time"
)

// LoggerMiddleware 是一個(gè)日志記錄中間件
func LoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 記錄請(qǐng)求開始時(shí)的信息
        startTime := time.Now()

        // 調(diào)用后續(xù)的處理函數(shù)
        c.Next()

        // 記錄請(qǐng)求結(jié)束時(shí)的信息
        endTime := time.Now()
        latency := endTime.Sub(startTime)

        // 記錄請(qǐng)求的IP地址和端口號(hào)
        ipAddress := c.ClientIP()

        // 記錄請(qǐng)求的URL
        requestUrl := c.Request.URL.Path

        // 記錄請(qǐng)求的方法
        httpMethod := c.Request.Method

        // 記錄請(qǐng)求的狀態(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)建一個(gè)默認(rèn)的Gin引擎

    // 注冊(cè)中間件
    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
    } // 啟動(dòng)服務(wù)器

}

請(qǐng)求

在這里插入圖片描述

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

在這里插入圖片描述

2. 身份驗(yàn)證中間件

身份驗(yàn)證中間件用于驗(yàn)證用戶的身份,如檢查令牌、Cookie等。

package middleware

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

// AuthMiddleware 是一個(gè)身份驗(yàn)證中間件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 假設(shè)我們使用Bearer令牌進(jìn)行身份驗(yàn)證
        token := c.GetHeader("Authorization")
        //查看token
        fmt.Println("查看token:", token)
        if token != "Bearer your_token_here" {
            //認(rèn)證未通過(guò),返回401狀態(tài)碼和錯(cuò)誤消息
            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)建一個(gè)默認(rèn)的Gin引擎

    // 注冊(cè)中間件
    r.Use(middleware.AuthMiddleware())

    // 定義路由
    r.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "Hello, Authenticated User!"})
    })

    r.Run(":8080") // 啟動(dòng)服務(wù)器
}

在這里插入圖片描述

3. 限流中間件

限流中間件用于限制特定時(shí)間段內(nèi)允許的請(qǐng)求數(shù)量,以防止服務(wù)器過(guò)載。

package main

import (
    "github.com/gin-gonic/gin"
    "time"
)

var (
    limiter = NewLimiter(10, 1*time.Minute) // 設(shè)置限流器,允許每分鐘最多請(qǐng)求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                // 限制的請(qǐng)求數(shù)量
    duration   time.Duration      // 時(shí)間窗口
    timestamps map[string][]int64 // 請(qǐng)求的時(shí)間戳
}

// Middleware 限流中間件
func (l *Limiter) Middleware(c *gin.Context) {
    ip := c.ClientIP() // 獲取客戶端IP地址

    // 檢查請(qǐng)求時(shí)間戳切片是否存在
    if _, ok := l.timestamps[ip]; !ok {
        l.timestamps[ip] = make([]int64, 0)
    }

    now := time.Now().Unix() // 當(dāng)前時(shí)間戳

    // 移除過(guò)期的請(qǐng)求時(shí)間戳
    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--
        }
    }

    // 檢查請(qǐng)求數(shù)量是否超過(guò)限制
    if len(l.timestamps[ip]) >= l.limit {
        c.JSON(429, gin.H{
            "message": "Too Many Requests",
        })
        c.Abort()
        return
    }

    // 添加當(dāng)前請(qǐng)求時(shí)間戳到切片
    l.timestamps[ip] = append(l.timestamps[ip], now)

    // 繼續(xù)處理請(qǐng)求
    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)訪問(wèn)超過(guò)10次,就限流了

在這里插入圖片描述

以上就是Golang Gin框架中間件的用法詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang Gin框架中間件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論