浅谈gin中间件 21 August 2024 / 6 min read
中间件是什么
Wikipedia中间件
"Middleware is a type of computer software program that provides services to software applications beyond those available from the operating system. It can be described as "software glue"."
中间件本质是一段用于处理、修改,拦截Http请求和相应的代码,在许多Web框架中起到关键作用
在Gin中,中间件主要作用包括:
Gin中的中间件是怎么工作的
Gin中,中间件通过函数链的形式工作,每个中间件接受*gin.Context
对象,这个对象会在中间件链中进行传递。中间件会在请求到达最终处理函数之前执行一些逻辑,也可以在处理函数之后执行逻辑
以以下这段demo进行理解
func LoggerMiddleware ( c * gin . Context ) {
log. Println ( " Request received " )
c. Next () // 继续处理下一个中间件或处理函数
log. Println ( " Response sent " )
func AuthMiddleware ( c * gin . Context ) {
log. Println ( " Performing authentication " )
// 这里可以根据认证结果决定是否调用 c.Next()
c. Next () // 继续处理下一个中间件或处理函数
r. Use (LoggerMiddleware, AuthMiddleware)
r. GET ( " /hello " , func ( c * gin . Context ) {
log. Println ( " Handling request " )
c. JSON ( 200 , gin . H { " message " : " Hello, world! " })
Demo执行流程
LoggerMiddleware
中间件开始,先输出”Request received”
执行日志中间件的C.Next()
进入认证中间件的模拟认证逻辑
执行认整中间件的C.Next()
调用路由的处理函数,生成并发送 “Hello, world!” 响应
响应发送后,先执行认证中间件C.Next()
后的代码,因为在中间件中没有后续的操作所以直接进入下一步
执行日志中间件c.Next()
之后的”Respose sent”
在LoggerMiddleware
中间件开始时,会先输出”Request received”,在执行c.Next()
后,Gin会继续调用下一个中间件或处理函数,此时LoggerMiddleware
中间件的log.Println("Response sent")
还未被执行,
总结
请求到达
依次执行中间件
调用处理函数
响应返回
Gin中间件源码分析
Gin中,中间件的实现依靠责任链模式,这个设计模式使得中间件可以串联起来,依次处理请求。
在Gin的默认路由中,会给你默认注册两个中间件,用于日志记录,以及捕获和处理Panic
debugPrintWARNINGDefault ()
// New用于注册一个新的路由,不带任何默认的中间件
engine. Use ( Logger (), Recovery ())
以下内容参考 一文讲透gin中间件使用及源码解析
从上述Demo中可以看出,Gin中注册中间件使用的是Use
方法。
func ( engine * Engine ) Use ( middleware ... HandlerFunc ) IRoutes {
engine.RouterGroup. Use (middleware ... )
engine. rebuild404Handlers ()
engine. rebuild405Handlers ()
它可用于接受一个边长参数middleware
,而这个参数的类型,实际上就是HandlerFunc
切片。
type HandlerFunc func ( * Context )
继续查看engine.RouterGroup.Use(middleware...)
这个路由组中的Use
func ( group * RouterGroup ) Use ( middleware ... HandlerFunc ) IRoutes {
group.Handlers = append (group.Handlers, middleware ... )
发现它其实就是将中间件HandlerFunc
追加到group.Handlers
中,而在注册路由的时候,会将对应的路由函数和中间件函数结合到一起,最终按链的顺序执行
type HandlersChain [] HandlerFunc
func ( group * RouterGroup ) handle ( httpMethod , relativePath string , handlers HandlersChain ) IRoutes {
absolutePath := group. calculateAbsolutePath (relativePath)
handlers = group. combineHandlers (handlers) // 将处理请求的函数与中间件函数结合
group.engine. addRoute (httpMethod, absolutePath, handlers)
其中结合操作的函数内容如下,注意观察这里是如何实现拼接两个切片得到一个新切片的
const abortIndex int8 = math.MaxInt8 / 2
func ( group * RouterGroup ) combineHandlers ( handlers HandlersChain ) HandlersChain {
finalSize := len (group.Handlers) + len (handlers)
if finalSize >= int (abortIndex) { // 这里有一个最大限制
panic ( " too many handlers " )
mergedHandlers := make ( HandlersChain , finalSize)
copy (mergedHandlers, group.Handlers)
copy (mergedHandlers[ len (group.Handlers):], handlers)
而关于中间件的执行,Gin提供了四个接口
c.Next()
继续执行下一个中间件或处理函数,如不调用,则处理链会被中断,后续的中间件或处理函数不会被执行
c.Abort()
用于中止请求的处理流程,后续的中间件和处理函数不会被执行
可以用于中间件中组织请求的进一步处理,比如返回身份验证失败的错误响应
c.Set(key stirng, value infetface)
可以存储一个键值对到*gin.Context
用于在链处理的不同阶段共享数据给中间件或处理函数
c.Get(key string)
从*gin.Context
中获取指定的键值对
参考内容
一文讲透gin中间件使用及源码解析 - 大师兄技术私享