Go-Gin中间件初探

1 前言

中间件,顾名思义,它是一个处于中间的组件,谁和谁的中间呢?一般是请求入口和执行方法中间。

在Gin的整个实现中,中间件可谓是Gin的精髓。一个个中间件组成一条中间件链,对HTTP Request请求进行拦截处理,实现了代码的解耦和分离,并且中间件之间相互不用感知到,每个中间件只需要处理自己需要处理的事情即可。今天我们就通过这篇文章,详细的介绍Gin中间的使用和原理。

2 Gin的默认中间件

在Gin中,我们可以通过Gin提供的默认函数,来构建一个自带默认中间件的*Engine

r := gin.Default()
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

可以看到 Default() 返回了一个带着Logger和Recovery中间件的引擎实例。

// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
	RouterGroup
    .
    .
    .
}

引擎是框架的实例,包含了muxer,中间件和配置。

Engine.RouteGroup 是一个路由分组,用来内部配置路由,与 一个路由前缀 和 一组 handlers(中间件 )联系起来。

// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

Engine.RouteGroup.Handles 是一个 HandlerFunc 列表;

// HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc

HandlerFunc 是一个函数,它定义了handler,这个handler被gin中间件当做返回值来使用。 

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

 

从上面的代码可以看出,使用 gin.Default() 会返回一个框架实例,这个实例带有 路由分组,而路由分组含有一组中间件gin.HandlerFunc,每个中间件实际上就是一个以 *Context 为参数的函数。

 

engine.Use(Logger(), Recovery())
// Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

而 engine 是通过 .Use(middleware ...HandlerFunc) 来把 不定多个 HandlerFunc 串起来用的,也就意味着,我们可以从前到后,先后使用多个中间件。

3 自定义中间件

我们知道一个中间件就是 gin.HandlerFunc,就是一个以 *Context 为参数的函数。那我们尝试着自己定义一个中间件。

func costTime() gin.HandlerFunc {
	return func(c *gin.Context) {
		//请求前获取当前时间
		nowTime := time.Now()

		//请求处理
		c.Next()

		//处理后获取消耗时间
		costTime := time.Since(nowTime)
		url := c.Request.URL.String()
		fmt.Printf("the request URL %s cost %v\n", url, costTime)
	}
}

我们看到,costTime() 返回的是一个 以 *gin.Context 为参数的函数,也就是 gin.HandlerFunc 结构。它在执行了 c.Next() 方法后,打印出消耗时间。

这里的 c.Next() 方法,是去执行后续中间件请求处理,含当前中间件之后的、还未执行的中间件,以及我们定义的处理方法。

我们来试验一下:

package main

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

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

	r.Use(costTime())

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, "首页")
	})

	r.Run(":8080")
}

func costTime() gin.HandlerFunc {
	return func(c *gin.Context) {
		//请求前获取当前时间
		nowTime := time.Now()

		//请求处理
		c.Next()

		//处理后获取消耗时间
		costTime := time.Since(nowTime)
		url := c.Request.URL.String()
		fmt.Printf("the request URL %s cost %v\n", url, costTime)
	}
}

记录

[GIN-debug] GET    /                         --> main.main.func1 (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8080
the request URL / cost 603.209µs
[GIN] 2023/03/21 - 22:14:54 | 200 |     715.917µs |             ::1 | GET      "/"

这里可以看到 the request URL / cost 603.209µs 这个日志。

在日志的第一行,我们注意到 4 handlers 这些信息,也就是说,这个请求经历过了4个 handlers,我们整理一下:

  • gin.Default() 里面框架引擎默认Use 的 Logger() 和 Recovery() 中间件;
  • 被Use 的 自定义 costTime() 中间件;
  • 用来处理的 GET / 请求的 
    func(c *gin.Context) {
        c.JSON(200, "首页")
    }

这更加证明了,handler 就是一个以 *Context 为参数的函数 这个概念。

 

至此,我们初步对Gin的中间件有了一点了解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章