Gin中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

定义中间件

Gin中的中间件必须是一个 gin.HandlerFunc 类型。

c.Next()

调用后续的处理函数

记录接口耗时的中间件

定义一个统计请求耗时的中间件 m1

go 复制代码
package main

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

// HandlerFunc
func indexHandler(c *gin.Context) {
	fmt.Println("index")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index",
	})
}

// 定义一个中间件:统计请求处理函数的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	// 计时
	start := time.Now()
	c.Next()
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func main() {
	r := gin.Default()
	// GET(relativePath string, handlers ...HandlerFuncs) IRouter 
	r.GET("/index", m1, indexHandler)

	r.Run(":9090")
}


全局注册1个中间件

go 复制代码
func main() {
	r := gin.Default()
	r.Use(m1) // 全局注册中间件函数m1

	// GET(relativePath string, handlers ...HandlerFuncs() IRouter
	r.GET("/index", indexHandler)
	r.GET("/shop", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "shop",
		})
	})
	r.GET("/user", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "user",
		})
	})
	r.Run(":9090")
}




全局注册2个中间件


go 复制代码
package main

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

// HandlerFunc
func indexHandler(c *gin.Context) {
	fmt.Println("index")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index",
	})
}

// 定义一个中间件:统计请求处理函数的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	// 计时
	start := time.Now()
	c.Next() // 调用后续的处理函数
	//c.Abort() // 阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	c.Next()
	fmt.Println("m2 out...")
}

func main() {
	r := gin.Default()
	r.Use(m1, m2) // 全局注册中间件函数m1, m2

	// GET(relativePath string, handlers ...HandlerFuncs() IRouter
	r.GET("/index", indexHandler)
	r.Run(":9090")
}


c.Abort()

阻止调用后续的处理函数

go 复制代码
package main

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

// HandlerFunc
func indexHandler(c *gin.Context) {
	fmt.Println("index")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index",
	})
}

// 定义一个中间件:统计请求处理函数的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	// 计时
	start := time.Now()
	c.Next() // 调用后续的处理函数
	//c.Abort() // 阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	c.Abort()
	fmt.Println("m2 out...")
}

func main() {
	r := gin.Default()
	r.Use(m1, m2) // 全局注册中间件函数m1, m2

	// GET(relativePath string, handlers ...HandlerFuncs() IRouter
	r.GET("/index", indexHandler)
	r.Run(":9090")
}

return

go 复制代码
func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	c.Abort()
	return
	fmt.Println("m2 out...")
}

高阶中间件

go 复制代码
// 接受一个布尔参数 doCheck 来决定是否进行检查
// 如果 doCheck 为 true,可以在中间件中添加检查
// 如果为 false,则直接调用 c.Next(),继续请求处理
// 使得可以灵活控制是否需要验证
func authMiddleware(doCheck bool) gin.HandlerFunc {
	// 连接数据库
	// 或者一些其他准备工作
	return func(c *gin.Context) {
		if doCheck {
			// 是否登录的判断
			// if 是登录用户
			// c.Next()
			// else
			// c.Abort()
		} else {
			c.Next()
		}
	}
}
go 复制代码
func main() {
	r := gin.Default()
	
	r.Use(m1, m2, authMiddleware(false))

	// GET(relativePath string, handlers ...HandlerFuncs() IRouter
	r.GET("/index", indexHandler)
	r.Run(":9090")
}


记录响应体的中间件

有时候可能会想要记录下某些情况下返回给客户端的响应数据,这个时候就可以编写一个中间件来搞定。

go 复制代码
type bodyLogWriter struct {
	gin.ResponseWriter               // 嵌入gin框架ResponseWriter
	body               *bytes.Buffer // 我们记录用的response
}

// Write 写入响应体数据
func (w bodyLogWriter) Write(b []byte) (int, error) {
	w.body.Write(b)                  // 我们记录一份
	return w.ResponseWriter.Write(b) // 真正写入响应
}

// ginBodyLogMiddleware 一个记录返回给客户端响应体的中间件
// https://stackoverflow.com/questions/38501325/how-to-log-response-body-in-gin
func ginBodyLogMiddleware(c *gin.Context) {
	blw := &bodyLogWriter{body: bytes.NewBuffer([]byte{}), ResponseWriter: c.Writer}
	c.Writer = blw // 使用我们自定义的类型替换默认的

	c.Next() // 执行业务逻辑

	fmt.Println("Response body: " + blw.body.String()) // 事后按需记录返回的响应
}

跨域中间件cors

推荐使用社区的https://github.com/gin-contrib/cors 库,一行代码解决前后端分离架构下的跨域问题。

注意: 该中间件需要注册在业务处理函数前面。

这个库支持各种常用的配置项,具体使用方法如下。

go 复制代码
package main

import (
  "time"

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

func main() {
  router := gin.Default()
  // CORS for https://foo.com and https://github.com origins, allowing:
  // - PUT and PATCH methods
  // - Origin header
  // - Credentials share
  // - Preflight requests cached for 12 hours
  router.Use(cors.New(cors.Config{
    AllowOrigins:     []string{"https://foo.com"},  // 允许跨域发来请求的网站
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE",  "OPTIONS"},  // 允许的请求方法
    AllowHeaders:     []string{"Origin", "Authorization", "Content-Type"},
    ExposeHeaders:    []string{"Content-Length"},
    AllowCredentials: true,
    AllowOriginFunc: func(origin string) bool {  // 自定义过滤源站的方法
      return origin == "https://github.com"
    },
    MaxAge: 12 * time.Hour,
  }))
  router.Run()
}

当然可以简单的像下面的示例代码那样使用默认配置,允许所有的跨域请求。

go 复制代码
func main() {
  router := gin.Default()
  // same as
  // config := cors.DefaultConfig()
  // config.AllowAllOrigins = true
  // router.Use(cors.New(config))
  router.Use(cors.Default())
  router.Run()
}

注册中间件

在gin框架中,可以为每个路由添加任意数量的中间件。

为全局路由注册

go 复制代码
func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	// 注册一个全局中间件
	r.Use(StatCost())
	
	r.GET("/test", func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
	r.Run()
}

为某个路由单独注册

go 复制代码
// 给/test2路由单独注册中间件(可注册多个)
	r.GET("/test2", StatCost(), func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})

为路由组注册中间件

为路由组注册中间件有以下两种写法。

写法1

go 复制代码
shopGroup := r.Group("/shop", StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

写法2

go 复制代码
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

中间件注意事项

gin默认中间件

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

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

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

gin中间件中使用 goroutine

当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本( c.Copy() )。

相关推荐
谷大羽1 天前
Kafka Stream实战教程
spring boot·后端·中间件·kafka·stream
隔着天花板看星星1 天前
Kafka-创建topic源码
大数据·分布式·中间件·kafka
0x派大星2 天前
【Golang】——Gin 框架中的 API 请求处理与 JSON 数据绑定
开发语言·后端·golang·go·json·gin
get2002 天前
Gin 框架中间件详细介绍
中间件·gin
一水鉴天2 天前
智能工厂的设计软件 为了监管控一体化的全能Supervisor 的监督学习 之 序5 架构for认知系统 总述 (架构全图)
人工智能·学习·中间件·架构
bigbig猩猩2 天前
Gin 框架中的表单处理与数据绑定
驱动开发·gin
谢尔登3 天前
【Next】中间件
服务器·javascript·中间件
极客先躯3 天前
高级java每日一道面试题-2024年11月09日-缓存中间件篇-Redis和Memecache有什么区别?
java·缓存·中间件·每日一道面试题·高级java·缓存中间件篇
荣~博客3 天前
Golang语言整合jwt+gin框架实现token
开发语言·golang·gin
拧螺丝专业户3 天前
gin源码阅读(2)请求体中的JSON参数是如何解析的?
前端·json·gin