Golang Gin框架中间件的用法详解

 更新时间:2024年10月22日 09:06:24   作者:景天科技苑  
中间件是Gin框架中的一个核心概念,它允许开发者在处理HTTP请求的过程中插入自定义的钩子函数,从而实现诸如日志记录、身份验证、权限控制等公共逻辑,本文将结合实际案例,详细讲解Gin框架中间件的用法

一、中间件的基本概念

中间件是一个函数,它在HTTP请求处理的生命周期中的某个特定点被调用,可以对请求和响应进行预处理或后处理。中间件的主要功能包括日志记录、身份验证、权限控制、跨域资源共享(CORS)、参数处理、错误处理等。

日志记录:记录请求的详细信息,如请求路径、请求体等。
身份验证:验证用户的身份,如检查令牌、Cookie等。
权限控制:确定用户是否有权访问特定的资源或执行特定的操作。
跨域资源共享(CORS):允许不同域之间的资源共享。
参数处理:提取URL中的参数或请求体中的数据。
错误处理:捕获并处理请求处理过程中的错误。

在Gin框架中,中间件必须是一个gin.HandlerFunc类型的函数。中间件函数接受一个*gin.Context参数,并可以选择返回一个gin.HandlerFunc。中间件函数的典型结构如下:

func myHandler() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
        c.Set("userSession", "userid-1")
        //c.Next() // 放行,默认就会放行
        c.Abort() // 拦截,到这里就不会往下执行请求了
        fmt.Println("HandlerFunc-info")
    }
}

二、中间件的两个专属方法

1. ctx.Next() 继续

在程序进入中间件的时候需要先进行一些处理,然后去 执行核心业务,在执行完核心业务之后再回来执行该中间件。

2. ctx.Abort() 中断

在程序进入中间件之后我们进行了一些操作,判断该用户不满足访问这个请求的条件,这个时候我们就需要终止这个请求,不让其继续执行,这个时候就使用到了Abort

三、注册中间件

在Gin框架中,可以通过全局注册或单个路由中注册,路由组注册的方式来使用中间件。

1、定义一个我们自己的 HandlerFunc 如上

2、注册全局路由

所有的请求都会经过这里来处理
全局中间件会被应用到所有的路由上。使用r.Use()函数来注册全局中间件。

package main

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

// 定义一个中间件函数
func myHandler() gin.HandlerFunc {
    //返回一个gin.HandlerFunc函数
    return func(c *gin.Context) {
        // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
        // func (c *Context) Set(key string, value any)
        c.Set("userSession", "userid-1")
        //c.Next()  // 放行,默认就会放行
        //c.Abort() // 拦截,到这里就不会往下执行请求了  可以通过Abort做判定,控制请求的走向
        fmt.Println("HandlerFunc-info")
    }
}

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

    // 注册 全局的 HandlerFunc
    // func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
    // 注意,这里面的参数是我们定义的中间件函数的执行
    ginServer.Use(myHandler())

    // 接收请求
    ginServer.GET("/test", func(c *gin.Context) {
        // 从上下文取值 拿到我们在中间件中设置的值
        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请求拿到数据

在这里插入图片描述

post请求也拿到数据

在这里插入图片描述

3、为某个路由单独注册

为单个路由注册的中间件,只能在该路由中生效

package main

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

// 定义一个中间件函数
func myHandler() gin.HandlerFunc {
    //返回一个gin.HandlerFunc函数
    return func(c *gin.Context) {
        // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
        // func (c *Context) Set(key string, value any)
        c.Set("userSession", "userid-1")
        //c.Next()  // 放行,默认就会放行
        //c.Abort() // 拦截,到这里就不会往下执行请求了  可以通过Abort做判定,控制请求的走向
        fmt.Println("HandlerFunc-info")
    }
}

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

    // 单路由注册中间件 ,只能在该路由中生效
    ginServer.GET("/test", myHandler(), func(c *gin.Context) {
        // 从上下文取值 拿到我们在中间件中设置的值
        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) {
        //当没有在该路由中注册中间件时,获取中间件中的变量值,获取不到会报panic
        namePost := c.MustGet("userSession").(string)
        log.Println(namePost)
        c.JSON(http.StatusOK, gin.H{
            "myname": namePost,
        })
    })

    err := ginServer.Run()
    if err != nil {
        return
    }

}

在这里插入图片描述

当没有在该路由中注册中间件时,获取中间件中的变量值,获取不到会报panic

在这里插入图片描述

拿不到值报异常

在这里插入图片描述

4、为路由组注册中间件

路由组中间件只会被应用到该路由组下的路由上。通过创建一个路由组,并使用v1.Use()函数为该路由组注册中间件。

package main

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

// 定义一个中间件函数
func myHandler() gin.HandlerFunc {
    //返回一个gin.HandlerFunc函数
    return func(c *gin.Context) {
        // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
        // func (c *Context) Set(key string, value any)
        c.Set("userSession", "userid-1")
        //c.Next()  // 放行,默认就会放行
        //c.Abort() // 拦截,到这里就不会往下执行请求了  可以通过Abort做判定,控制请求的走向
        fmt.Println("HandlerFunc-info")
    }
}

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

    //创建路由组
    v1 := ginServer.Group("/v1") // 创建一个名为/v1的路由组
    //路由组应用中间件
    v1.Use(myHandler())

    // 路由组中间件
    {
        v1.GET("/test", func(c *gin.Context) {
            // 从上下文取值 拿到我们在中间件中设置的值
            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) {
            //当没有在该路由中注册中间件时,获取中间件中的变量值,获取不到会报panic
            namePost := c.MustGet("userSession").(string)
            log.Println(namePost)
            c.JSON(http.StatusOK, gin.H{
                "myname": namePost,
            })
        })
    }

    err := ginServer.Run()
    if err != nil {
        return
    }

}

在上述代码中,我们创建了一个名为/v1的路由组,并为这个路由组注册了一个中间件。这个中间件只会被应用到/v1下的路由上。

路由组中间件也可以直接应用在Group方法中

v1 := r.Group("/test", myHandler())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

5、gin默认中间件

gin.Default()默认使用了LoggerRecovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

三、实际应用案例

以下将通过几个实际案例来演示Gin框架中间件的用法。

1. 日志记录中间件

日志记录中间件用于记录请求的详细信息,如请求路径、请求体、响应状态码等。

package middleware

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

// LoggerMiddleware 是一个日志记录中间件
func LoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 记录请求开始时的信息
        startTime := time.Now()

        // 调用后续的处理函数
        c.Next()

        // 记录请求结束时的信息
        endTime := time.Now()
        latency := endTime.Sub(startTime)

        // 记录请求的IP地址和端口号
        ipAddress := c.ClientIP()

        // 记录请求的URL
        requestUrl := c.Request.URL.Path

        // 记录请求的方法
        httpMethod := c.Request.Method

        // 记录请求的状态码
        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() // 创建一个默认的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
    } // 启动服务器

}

请求

在这里插入图片描述

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

在这里插入图片描述

2. 身份验证中间件

身份验证中间件用于验证用户的身份,如检查令牌、Cookie等。

package middleware

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

// AuthMiddleware 是一个身份验证中间件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 假设我们使用Bearer令牌进行身份验证
        token := c.GetHeader("Authorization")
        //查看token
        fmt.Println("查看token:", token)
        if token != "Bearer your_token_here" {
            //认证未通过,返回401状态码和错误消息
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized access"})
            c.Abort()
            return
        }

        // 调用后续的处理函数
        c.Next()

    }
}

在main.go中使用该中间件:

package main

import (
    "github.com/gin-gonic/gin"
    "jingtian/jt_gin/middleware"
    "net/http"
)

func main() {
    r := gin.Default() // 创建一个默认的Gin引擎

    // 注册中间件
    r.Use(middleware.AuthMiddleware())

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

    r.Run(":8080") // 启动服务器
}

在这里插入图片描述

3. 限流中间件

限流中间件用于限制特定时间段内允许的请求数量,以防止服务器过载。

package main

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

var (
    limiter = NewLimiter(10, 1*time.Minute) // 设置限流器,允许每分钟最多请求10次
)

// NewLimiter 创建限流器
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                // 限制的请求数量
    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() // 当前时间戳

    // 移除过期的请求时间戳
    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--
        }
    }

    // 检查请求数量是否超过限制
    if len(l.timestamps[ip]) >= l.limit {
        c.JSON(429, gin.H{
            "message": "Too Many Requests",
        })
        c.Abort()
        return
    }

    // 添加当前请求时间戳到切片
    l.timestamps[ip] = append(l.timestamps[ip], now)

    // 继续处理请求
    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")
}

每分钟内访问超过10次,就限流了

在这里插入图片描述

以上就是Golang Gin框架中间件的用法详解的详细内容,更多关于Golang Gin框架中间件的资料请关注脚本之家其它相关文章!

相关文章

  • Go mod包管理工具详解

    Go mod包管理工具详解

    Go mod作为Go语言的官方包管理工具,可以帮助开发者更好地管理包和依赖,提高开发效率和项目可维护性,本文将介绍Go语言的包和依赖管理,以及Go mod的作用和优势,需要的朋友可以参考下
    2023-05-05
  • Go语言流程控制之goto语句与无限循环

    Go语言流程控制之goto语句与无限循环

    这篇文章主要介绍了Go语言流程控制之goto语句与无限循环,是golang入门学习中的基础知识,需要的朋友可以参考下
    2015-10-10
  • Golang 实现分片读取http超大文件流和并发控制

    Golang 实现分片读取http超大文件流和并发控制

    这篇文章主要介绍了Golang 实现分片读取http超大文件流和并发控制,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • ​​​​​​​Golang实现RabbitMQ中死信队列几种情况

    ​​​​​​​Golang实现RabbitMQ中死信队列几种情况

    本文主要介绍了​​​​​​​Golang实现RabbitMQ中死信队列几种情况,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Go扩展原语之SingleFlight的用法详解

    Go扩展原语之SingleFlight的用法详解

    Go语言扩展包同步原语singleflight.Group能够再一个服务中抑制对下游的多次重复请求,它能够限制对同一个键值对的多次重复请求,减少对下游的瞬时流量,接下来小编就给大家讲讲Go SingleFlight的具体用法,需要的朋友可以参考下
    2023-07-07
  • Golang内存泄漏场景以及解决方案详析

    Golang内存泄漏场景以及解决方案详析

    golang中内存泄露的发现与排查一直是来是go开发者头疼的一件事,下面这篇文章主要给大家介绍了关于Golang内存泄漏场景以及解决的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • Go语言中strings.HasPrefix、strings.Split、strings.SplitN() 函数

    Go语言中strings.HasPrefix、strings.Split、strings.SplitN() 函数

    本文主要介绍了Go语言中strings.HasPrefix、strings.Split、strings.SplitN()函数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • 详解Golang中SQLX库的高级操作

    详解Golang中SQLX库的高级操作

    sqlx是Golang中的一个知名三方库,其为Go标准库database/sql提供了一组扩展支持,下面就来和大家分享一下SQLX库的高级操作吧,希望对大家有所帮助
    2023-06-06
  • Go语言通过Luhn算法验证信用卡卡号是否有效的方法

    Go语言通过Luhn算法验证信用卡卡号是否有效的方法

    这篇文章主要介绍了Go语言通过Luhn算法验证信用卡卡号是否有效的方法,实例分析了Luhn算法的原理与验证卡号的使用技巧,需要的朋友可以参考下
    2015-03-03
  • 一文搞懂Golang中iota的用法和原理

    一文搞懂Golang中iota的用法和原理

    我们知道iota是go语言的常量计数器,本文尝试全面总结其使用用法以及其实现原理,需要的朋友可以参考以下内容,希望对大家有所帮助
    2022-08-08

最新评论