Gin框架中间件原理

先了解闭包

闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境

func adder() func(int) int {var x intreturn func(y int) int {x += yreturn x}
}
func main() {var f = adder()fmt.Println(f(10)) //10fmt.Println(f(20)) //30fmt.Println(f(30)) //60f1 := adder()fmt.Println(f1(40)) //40fmt.Println(f1(50)) //90
}

Gin框架中中间件

在Gin框架中,中间件(Middleware)是在处理请求的过程中,在请求被真正的处理函数处理之前或之后执行的一些逻辑。

其原理主要是通过函数式编程和Go语言的闭包特性来实现的。

当定义一个中间件时,实际上是定义了一个函数,这个函数接收gin.Context作为参数。例如:

func Logger() gin.HandlerFunc {return func(c *gin.Context) {// 记录请求开始时间t := time.Now()// 调用下一个中间件或者处理函数c.Next()// 计算请求耗时并打印日志latency := time.Since(t)log.Printf("%s %s %v", c.Request.Method, c.Request.URL.Path, latency)}
}

在这个中间件函数内部,通过c.Next()来调用下一个中间件或者最终的处理函数。

当使用Use方法添加中间件到Gin引擎时,Gin会将这些中间件函数组成一个链式结构。当一个请求到来时,Gin会从链头开始逐个执行中间件函数。在每个中间件函数中,可以在c.Next()调用之前进行前置操作,比如验证用户身份、记录日志等;在c.Next()调用之后进行后置操作,比如添加响应头、记录响应时间等。这样就实现了对请求处理过程的拦截和增强。

具体实现

以下是Gin框架将中间件函数组成链式结构的具体实现原理及相关细节:

1. 核心数据结构

在Gin框架中,有一个关键的结构体(简化示意),类似如下:

type Engine struct {// 用于存储中间件函数的切片middlewares []gin.HandlerFunc// 路由树等其他相关结构,这里重点关注中间件相关部分//...
}

Engine结构体中的middlewares切片就是用来存放通过Use方法添加进来的中间件函数的,每个中间件函数都是gin.HandlerFunc类型,它的定义本质上是一个接收*gin.Context作为参数并返回void的函数类型,例如:

type HandlerFunc func(*gin.Context)

2. Use方法的作用

当调用EngineUse方法来添加中间件时,代码逻辑大致如下(简化版):

func (engine *Engine) Use(middleware...gin.HandlerFunc) {engine.middlewares = append(engine.middlewares, middleware...)
}

Use方法就是简单地将传入的中间件函数一个个追加到middlewares切片中,这样就完成了中间件的收集,为后续构建链式结构做准备。

3. 构建链式结构(核心在于请求处理流程)

当有请求进来时,Gin会从已添加的中间件链头开始逐个执行中间件函数,这个过程是在处理请求的主逻辑中实现的。在Gin内部处理请求的函数中(简化示意如下):

func (engine *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {c := engine.pool.Get().(*gin.Context)c.Writer = &responseWriter{Writer: w}c.Request = r// 遍历中间件切片,构建链式调用h := engine.combineHandlers()h(c)engine.pool.Put(c)
}

关键在于engine.combineHandlers()这个方法,它会将middlewares切片中的中间件函数组合起来,返回一个最终的处理函数(也是gin.HandlerFunc类型),组合的方式大概是通过闭包和函数嵌套来实现类似如下逻辑(简化示意,实际更复杂):

func (engine *Engine) combineHandlers() gin.HandlerFunc {return func(c *gin.Context) {var currentIndex intvar nextHandler func()// 定义下一个中间件或最终处理函数的执行逻辑nextHandler = func() {if currentIndex < len(engine.middlewares) {middleware := engine.middlewares[currentIndex]currentIndex++// 调用当前中间件,并在当前中间件内部调用nextHandler来执行后续的中间件或最终处理函数middleware(c)} else {// 如果中间件都执行完了,执行路由对应的最终处理函数(比如某个具体的控制器方法)engine.handleContext(c)}}// 启动链式调用,从第一个中间件开始nextHandler()}
}

在上述代码中:

  • 通过定义nextHandler函数,它会根据当前中间件的索引来决定是调用下一个中间件还是执行最终的路由处理函数(engine.handleContext(c)部分,这里省略其具体细节,它主要涉及根据路由匹配去执行对应的业务逻辑处理函数)。
  • 每个中间件函数内部通过调用c.Next()其实就是间接调用了这个nextHandler函数,从而实现了控制权在中间件之间以及最终处理函数之间的有序转移,构建起了链式结构,使得请求在处理过程中会依次经过各个中间件,并且在合适的时机执行后续操作或者返回响应。

总的来说,Gin借助切片收集中间件函数,然后在请求处理阶段通过巧妙的函数嵌套、闭包以及对函数执行顺序的控制,实现了将中间件函数组成链式结构来处理请求的机制。

理解gin中间件和go直接作为http server的中间件有什么区别?

Gin中间件实现原理总结

  • Gin中间件是基于Go语言的函数式编程和闭包来实现的。它主要围绕gin.Context进行操作。gin.Context包含了请求和响应的所有信息,如请求方法、请求路径、请求头、响应状态码等。
  • 链式调用:Gin通过将多个中间件函数组合成一个链来工作。当一个请求到达时,它会按添加中间件的顺序依次执行这些中间件。每个中间件函数可以选择在调用c.Next()之前执行一些前置操作,如权限验证、日志记录开始时间等。
  • 控制权转移c.Next()函数是关键。当执行到c.Next()时,它会暂停当前中间件的执行,将控制权转移给下一个中间件或者最终的处理函数。在所有中间件(以及最终处理函数)执行完返回后,c.Next()之后的代码会继续执行,用于完成后置操作,如记录响应时间、修改响应头等。

与Go直接作为http server中间件的区别

  • 抽象程度不同
    • Go原生http中间件:在Go原生的http包中写中间件,需要直接操作http.Requesthttp.ResponseWriter。这两个对象提供了相对底层的对HTTP请求和响应的操作接口,开发者需要自己处理诸如读取请求头、写入响应状态码等细节。
    • Gin中间件:Gin的中间件基于gin.Context,它对http.Requesthttp.ResponseWriter进行了封装,提供了更高级、更方便的API。例如,可以通过c.Query("param_name")直接获取URL查询参数,而不用像在原生http中那样手动解析。
  • 中间件组织形式不同
    • Go原生http中间件:在Go原生http中,若要构建中间件链,通常需要自己手动调用下一个中间件函数。这可能会导致代码结构不够清晰,特别是当中间件数量较多时,容易出现混乱。
    • Gin中间件:Gin通过Use方法将中间件添加到引擎,自动构建中间件链。这种方式更加清晰、简洁,使得中间件的管理和复用更加方便。
  • 功能丰富度不同
    • Go原生http中间件:Go原生的中间件功能相对基础,主要聚焦于对HTTP基本操作的拦截和处理。
    • Gin中间件:Gin中间件除了基本的HTTP操作外,还提供了许多与Web开发密切相关的功能。比如,Gin的中间件可以很方便地集成日志记录、错误处理、权限验证等多种功能,并且这些功能可以通过Gin提供的插件或者自定义中间件轻松扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502984.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基础 注解

分类 Java自带的标准注解&#xff0c;包括Override、Deprecated和SuppressWarnings&#xff0c;分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告&#xff0c;用这些注解标明后编译器就会进行检查。元注解&#xff0c;元注解是用于定义注解的注解&#xff0…

Linux中rsync命令使用

一、rsync简介 rsync 是一种高效的文件复制和同步工具&#xff0c;常用于在本地或远程计算机之间同步文件和目录 主要特性增量同步&#xff1a;rsync 会检测源和目标文件之间的差异&#xff0c;只传输发生变化的部分&#xff0c;而不是重新传输整个文件。这样就能有效减少数据…

基于STM32的自动水满报警系统设计

目录 引言系统设计 硬件设计软件设计系统功能模块 水位检测模块报警模块自动控制模块控制算法 水位检测逻辑报警触发逻辑代码实现 水位检测模块报警控制模块自动控制逻辑系统调试与优化结论与展望 1. 引言 水满报警系统在家庭、农业、工业等领域广泛应用&#xff0c;通过实时…

【Java数据结构】二叉树

1.树型结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;由n个结点组成的具有层次关系的集合。下面是它的特点&#xff1a; 根结点是没有前驱的结点&#xff08;没有父结点的结点&#xff09;子结点之间互不相交除了根结点外&#xff0c;其它结点都只有一个父结点n个结…

学习threejs,导入AWD格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.AWDLoader AWD模型加…

Chapter4.3:Implementing a feed forward network with GELU activations

4 Implementing a GPT model from Scratch To Generate Text 4.3 Implementing a feed forward network with GELU activations 本节即将实现子模块&#xff0c;用于transformer block&#xff08;变换器块&#xff09;的一部分。为此&#xff0c;我们需要从激活函数开始。 深…

弥散张量分析开源软件 DSI Studio 简体中文汉化版可以下载了

网址&#xff1a; (63条消息) DSIStudio简体中文汉化版(2022年7月)-算法与数据结构文档类资源-CSDN文库

【信号滤波 (补充)】二阶陷波滤波代码推导过程(C++)

目录 二阶陷波滤波器计算实例一、 传递函数的参数推导1. 首先 b 0 , b 1 , b 2 b_0, b_1, b_2 b0​,b1​,b2​是怎么推导出来的&#xff1f;2. 带入实际值求解3. 验证上述的传递函数 二、将传递函数转化成差分方程2.1 传递函数写成输入输出形式2.2 Z域转化为时域 三、将差分方程…

C++进阶——用Hash封装unordered_map和unordered_set

目录 前言 源码怎么说 为什么要兼容&#xff1f; 兼容的具体做法&#xff1f; 为什么要把Key转为整数&#xff08;HashFcn&#xff09;&#xff1f; 模拟实现 一、建立框架 二、迭代器 运算符重载 迭代器兼容大法 三、[ ]重载 四、实现不能修改key 实现及测试代码 …

安装MySQL的五种方法(Linux系统和Windows系统)

一.在Linux系统中安装MySQL 第一种方法:在线YUM仓库 首先打开MySQL官网首页 www.mysql.com 找到【DOWNLOADS】选项&#xff0c;点击 下拉&#xff0c;找到 【MySQL Community(GPL) Downloads】 在社区版下载页面中&#xff0c;【 MySQL Yum Repository 】链接为在线仓库安装…

极客说|微软 Phi 系列小模型和多模态小模型

作者&#xff1a;胡平 - 微软云人工智能高级专家 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&am…

封装/前线修饰符/Idea项目结构/package/impore

目录 1. 封装的情景引入 2. 封装的体现 3. 权限修饰符 4. Idea 项目结构 5. package 关键字 6. import 关键字 7. 练习 程序设计&#xff1a;高内聚&#xff0c;低耦合&#xff1b; 高内聚&#xff1a;将类的内部操作“隐藏”起来&#xff0c;不需要外界干涉&#xff1b…

【C++】P5733 【深基6.例1】自动修正

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述&#x1f4af;解题思路概述&#x1f4af;第一种实现方式&#xff1a;直接使用字符ASCII值计算代码实现代码分析 &#x1f4af;第二种实现方式&#xff1a;直接修改…

【Elasticsearch】文档操作:添加、更新和删除

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

【insert 插入数据语法合集】.NET开源ORM框架 SqlSugar 系列

系列文章目录 &#x1f380;&#x1f380;&#x1f380; .NET开源 ORM 框架 SqlSugar 系列 &#x1f380;&#x1f380;&#x1f380; 文章目录 系列文章目录一、前言 &#x1f343;二、插入方式 &#x1f4af;2.1 单条插入实体2.2 批量 插入实体2.3 根据字典插入2.4 根据 Dat…

权限掩码umask

1 、 设置新建文件或目录的默认权限 在 Linux 系统中&#xff0c;当用户创建一个新的文件或目录时&#xff0c;系统都会为新建的文件或目录分配默认的权限&#xff0c;该默认权限与umask 值有关&#xff0c;其具体关系是&#xff1a; 新建文件的默认权限 0666-umask 值 新建…

202-01-06 Unity 使用 Tip1 —— UnityHub 模块卸载重装

文章目录 1 卸载模块2 更新配置文件3 重启 UnityHub 起因&#xff1a; ​ WebGL 平台打包程序报错&#xff0c;懒得修复了&#xff0c;因此粗暴地删了重装。但是 UnityHub 不支持卸载模块&#xff0c;因此手动配置。 1 卸载模块 ​ 以 Unity 6000.0.26f1c1 为例&#xff0c;其…

打造三甲医院人工智能矩阵新引擎(二):医学影像大模型篇--“火眼金睛”TransUNet

一、引言 1.1 研究背景与意义 在现代医疗领域,医学影像作为疾病诊断与治疗的关键依据,发挥着不可替代的作用。从传统的X射线、CT(计算机断层扫描)到MRI(磁共振成像)等先进技术,医学影像能够直观呈现人体内部结构,为医生提供丰富的诊断信息,涵盖疾病识别、病灶定位、…

国产编辑器EverEdit - 两种删除空白行的方法

1 使用技巧&#xff1a;删除空白行 1.1 应用场景 用户在编辑文档时&#xff0c;可能会遇到很多空白行需要删除的情况&#xff0c;比如从网页上拷贝文字&#xff0c;可能就会存在大量的空白行要删除。 1.2 使用方法 1.2.1 方法1&#xff1a; 使用编辑主菜单 选择主菜单编辑 …

李宏毅机器学习笔记-Transformer

目录 1. Seq2seq 2. encoder Transformer 中的 Block 结构 3. Decoder 4.Encoder和Decoder间的信息传递 5.Training 6.Tips 1. Seq2seq Transformer 是一个seq2seq的model。Seq2seq指的是input是一个序列&#xff0c;输出也是一个序列&#xff0c;输出的长度是由机器自己…