[每周一更]-(第83期):Go新项目-Gin中间件的使用和案例(10)

在 Gin 中,中间件是一种用于处理 HTTP 请求和响应的功能强大的机制。中间件是一段位于请求处理链和最终处理器之间的代码,

它可以截获请求、执行预处理操作,修改请求或响应,然后将控制权传递给下一个中间件或最终的请求处理器。

中间价在业务使用中,方便注入一些业务无关的逻辑,并且方便移植到特质的接口中;

下文会聊下:日志、ip限制、API请求速率控制、跨域请求;

以下是 Gin 中间件的概念和使用方式:

概念

执行顺序: Gin 中的中间件按照它们添加的顺序执行。先添加的中间件先执行。

中断处理链: 中间件可以选择中断处理链,阻止后续的中间件和处理器执行。

修改请求和响应: 中间件可以修改请求的内容,也可以修改响应的内容。这使得中间件非常灵活,可以用于日志记录、认证、跨域处理等多种用途。

1、日志

使用方式
  1. 引入 Gin 包:

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

  2. 定义中间件函数:

中间件函数是一个接受 *gin.Context 参数的函数。例如,一个简单的日志记录中间件:

复制代码
func LoggerMiddleware(c *gin.Context) {
   // 执行处理之前的逻辑
   fmt.Println("Start Request")

   // 继续处理链
   c.Next()

   // 处理之后的逻辑
   fmt.Println("End Request")
}
  1. 使用中间件:

在路由组或路由上使用中间件。以下是一个简单的使用示例:

复制代码
func main() {
   r := gin.Default()

   // 使用自定义中间件
   r.Use(LoggerMiddleware)

   // 路由定义
   r.GET("/ping", func(c *gin.Context) {
       c.String(200, "pong")
   })

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

在上面的例子中,LoggerMiddleware 将在每个请求前后打印日志。

自定义中间件

你可以通过编写函数来创建自定义中间件。以下是一个例子,演示了一个处理请求头的中间件:

复制代码
func CustomMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 在请求处理之前执行的逻辑
        c.Header("Custom-Header", "Custom Value")
        
        // 继续处理链
        c.Next()
        
        // 在请求处理之后执行的逻辑
    }
}

然后,你可以像下面这样使用自定义中间件:

复制代码
goCopy code
func main() {
    r := gin.Default()

    // 使用自定义中间件
    r.Use(CustomMiddleware())

    // 路由定义
    r.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })

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

这个自定义中间件简单地在每个请求中添加了一个自定义的响应头。

注:某些单独接口使用 中间件(一般都是全局设置)

复制代码
r.POST("demo-middleware",IP,Rate,demo.Action)

2、校验IP

调用开源库:GeoLite2-Country/GeoLite2-Country.mmdb,进行比对判断是否为国内IP

复制代码
package middleware

import (
    "net"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/oschwald/geoip2-golang"
)

// IP 校验ip,非国内ip禁止访问。采用geoip2-lite数据库,免费版本,不保证准确率
func IP() gin.HandlerFunc {
    return func(c *gin.Context) {
    db, err := geoip2.Open(config.Env.GetString("app.mmdb_path"))
    if err != nil {
        panic(err)
    }
    defer db.Close()

    ip := net.ParseIP(c.GetHeader("X-Real-IP"))
        ipInfo, err := db.Country(ip)
            if err == nil {
            if ipInfo.Country.IsoCode != "CN" && ipInfo.Country.IsoCode != "" {
                response.CustomErrorJson(c, http.StatusBadRequest, response.NOT_PERMISSION,
                "禁止非中国用户访问", map[string]any{"errors": "禁止非中国用户访问"})
                return
            }
        }
    }
    // 处理请求
    c.Next() //  处理请求
}

3、API接口速度控制

接口请求速度控制,防止恶意爬虫或者恶意请求数据,导致服务资源紧张;

复制代码
package middleware

import (
  "net/http"

  "github.com/gin-gonic/gin"
  "golang.org/x/time/rate"
)

var limiter *rate.Limiter

func init() {
  // 令牌桶大小为60,每秒产生1个token,即每分钟最多处理60个请求
  limiter = rate.NewLimiter(1, 60)
}

func Rate() gin.HandlerFunc {
  return func(ctx *gin.Context) {
    if !limiter.Allow() {
      response.CustomErrorJson(ctx, http.StatusBadRequest, response.RATE_LIMIT,
      "请求过于频繁,请稍后重试", map[string]any{"errors": "请求过于频繁,请稍后重试"})
      return
    }

    // 处理请求
    c.Next() //  处理请求
  }
}

4、跨域请求

复制代码
package middleware

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

func Cors() gin.HandlerFunc {
    return func(c *gin.Context) {
		method := c.Request.Method               // 请求方法
		origin := c.Request.Header.Get("Origin") // 请求头部
		var headerKeys []string                  // 声明请求头keys
		for k, _ := range c.Request.Header {
			headerKeys = append(headerKeys, k)
		}
		headerStr := strings.Join(headerKeys, ", ")
		if headerStr != "" {
			headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
		} else {
			headerStr = "access-control-allow-origin, access-control-allow-headers"
		}
		if origin != "" {
			c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Origin", "*")                                       // 这是允许访问所有域
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") // 服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
			//  header的类型
			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token,"+
				" Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, "+
				"Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, "+
				"Cache-Control, Content-Type, Pragma")
			// 允许跨域设置         可以返回其他子段
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, "+
				"Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
			c.Header("Access-Control-Max-Age", "172800")          // 缓存请求信息 单位为秒
			c.Header("Access-Control-Allow-Credentials", "false") //  跨域请求是否需要带cookie信息 默认设置为true
			c.Set("content-type", "application/json")             // 设置返回格式是json
		}

		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.JSON(http.StatusOK, "Options Request!")
		}
		// 处理请求
		c.Next() //  处理请求
	}
}

参考地址

相关推荐
张忠琳10 小时前
【Go 1.26.4】Golang Slice 深度解析
开发语言·后端·golang
念何架构之路10 小时前
消息中间件
中间件
都说名字长不会被发现11 小时前
Spring Boot Starter 中间件账号密码加密方案设计与实现
java·spring boot·后端·中间件
张忠琳1 天前
【Go 1.26.4】Golang Channel 深度解析
开发语言·后端·golang
张忠琳1 天前
【Go 1.26.4】Golang Map 深度解析
开发语言·后端·golang
瀚高PG实验室1 天前
java中间件无法连接数据库
java·数据库·中间件·瀚高数据库
何以解忧,唯有..1 天前
Go 语言安装与环境配置完整指南
开发语言·后端·golang
之歆1 天前
Day11_Express 深入解析:从中间件到项目实战
中间件·express
踏着七彩祥云的小丑1 天前
Go 学习第6天:结构体 + 切片 + range遍历
开发语言·学习·golang·go
码农飞哥1 天前
RocketMQ消费接口设计实战:为什么HTTP回调接口必须吞掉所有异常,始终返回成功?
网络协议·http·中间件·消息队列·rocketmq