go web框架 gin-gonic源码解读03————middleware
go web框架 gin-gonic源码解读03————middleware(context)
今天打完游戏有空整理整理之前看的gin的中间件设计,go的中间件设计相较于前两站还是蛮简单,蛮容易看懂的,所以顺便把context也一起写一下。
中间件是现在web服务里统一化拓展最常用的功能,,他是为了在我们的web服务中实现一些可重复使用,可组合的功能方法、可以让我们的 web逻辑在执行之前或者之后进行预处理,后处理,验证等操作。 在说中间件之前,我们先回忆一下之前几张看过的代码的。
**gin.go** 文件中gin为了实现http.Handler接口,而实现的ServeHTTP()方法
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {// 从对象池获取Context对象(对象池这里就不讲了,大家可以搜博客去看,这里只要大家会用就可以了)c := engine.pool.Get().(*Context)// 这三行其实就context的构造,传入http.ResponseWriter,*http.Requestc.writermem.reset(w)c.Request = reqc.reset()// 去我们的路由中查找响应的url并执行响应的逻辑engine.handleHTTPRequest(c)engine.pool.Put(c)
}
这里再顺便给大家介绍一下context,context实际上就是gin框架为了统一的参数管理包装的数据结构,他除了包含了我们每次访问ServeHTTP的传参(w http.ResponseWriter, req *http.Request)还包含了一些封装的web Response方法(调用就返回,非常好用),和一些请求获取参数的方法,其基本上覆盖了开发者平时所以操作需求,有兴趣的同学可以自己看看 .\Go\gin\context.go 中的内容。
// 这里缩略一下,只讲今天会用到的几个参数
type Context struct {writermem responseWriterRequest *http.RequestWriter ResponseWriterhandlers HandlersChain // 中间件执行链handlers index int8 // 执行下标fullPath string// 引擎的指针engine *Engine```略```
}
我们回到正题,看看我们的**handleHTTPRequest()**方法
// 去我们的路由中查找响应的url并执行响应的逻辑
engine.handleHTTPRequest( c )
这就是我们上次手撕的那个前缀树的查找方法。而我们的目的是通过收到http请求的url来找到,客户端需要请求的逻辑接口。
// 由于篇幅所限,代码会有所缩略func (engine *Engine) handleHTTPRequest(c *Context) {// 很显然就是把我们请求方法和路径从Context里拿出来,方便使用httpMethod := c.Request.MethodrPath := c.Request.URL.Pathunescape := false```略```// Find root of the tree for the given HTTP method// 这里就是我们的前缀树的t := engine.treesfor i, tl := 0, len(t); i < tl; i++ {// 这里前面说过的引擎(engine)中存的是一个前缀树的切片([]tree)// 每个请求方法一棵树,这里是遍历切片找到对应的请求方法if t[i].method != httpMethod {continue}root := t[i].root// Find route in tree// 找到树了,去树里查找value := root.getValue(rPath, c.params, c.skippedNodes, unescape)// 参数节点,拿参数if value.params != nil {c.Params = *value.params}// 关键点来了,这里就找到了我们要执行的逻辑方法if value.handlers != nil {c.handlers = value.handlersc.fullPath = value.fullPathc.Next()c.writermem.WriteHeaderNow()return}```略```}break}```略```serveError(c, http.StatusNotFound, default404Body)
}
为了讲的详细的点我们把我们的关键代码单独拿出来说
c.handlers = value.handlers // 1.将接口的handlers赋值给了Context的handlersc.fullPath = value.fullPath // 2.给fullPath 赋值c.Next() // 3.执行Next() 方法c.writermem.WriteHeaderNow()// 4. 给HTTP response写入status codereturn
步骤1的赋值给的是handlers 而不是handler,这里大家可能会很奇怪,我业务逻辑其实一个函数就可以解决,这里为啥会缓存一个handlers呢,难道我的业务函数要拆分成好几个函数来写?其实这个handlers 存储的除了我们业务函数就是我们所有的中间件函数。
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)// HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc
gin的中间件调用的密码也都包含在步骤3里
func (c *Context) Next() {// 执行下标++c.index++// 一上来就自增是因为func (c *Context) reset()这个初始化方法里,会把下标初始化成-1,所以我们一上来就要++,让他变成0。// 这么做的目的就是因为我们的中间件函数中也会调用Next()for c.index < int8(len(c.handlers)) {// 通过下标去执行中间件c.handlers[c.index](c)// 显然执行完了++,不然就死循环了c.index++}
}
gin的中间件调用的秘密还是蛮简单的,接下来我们看看gin中间件的注册。
gin的中间件注册,大家都知道engine可以使用Use,路由组的RouterGroup也可以使用Use,实际上engine.Use()也是调用了RouterGroup.Use(),因为我们engine的路由包括了我们所有的RouterGroup的路由。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {engine.RouterGroup.Use(middleware...)// 这边是初始化的时候顺便注册一些异常的处理方式// 当然为了避免重复的调用这两个rebuild40X函数,这个Use还是建议一次性调用到位engine.rebuild404Handlers() engine.rebuild405Handlers()return engine
}// Use adds middleware to the group, see example code in GitHub.
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {// 非常的简单,我们的Handlers 是一个切片,他把所有的middleware插入到中间件的末端group.Handlers = append(group.Handlers, middleware...)return group.returnObj()
}
这样子就实现了我们功能强大的中间件调用链条,只说这些感觉内容少了点,再带大家看看gin自带的两个中间件把。
// 这是gin代码中获取默认Engine 的方法,大家一定都用过
func Default() *Engine {// 无关紧要的版本校验debugPrintWARNINGDefault()// New()第一篇应该说过engine := New()// 这里调用了两个中间件Logger(), Recovery()engine.Use(Logger(), Recovery())return engine
}
Logger() 顾名思义,很显然是打印日志的一个中间件,打印日志也是中间件在开发中最有用的几个用处之一
func Logger() HandlerFunc {return LoggerWithConfig(LoggerConfig{})
}// LoggerWithConfig instance a Logger middleware with config.
func LoggerWithConfig(conf LoggerConfig) HandlerFunc {// LoggerConfig不是我们本文讲解的重点,大家可以自己去看看// 这下面的代码大意就是获取日志的打印格式和输出位置formatter := conf.Formatterif formatter == nil {formatter = defaultLogFormatter}out := conf.Outputif out == nil {out = DefaultWriter}notlogged := conf.SkipPathsisTerm := trueif w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {isTerm = false}// 知识点:struct{}不占内存空间// 在go语言map[string]struct{}的写法相当于创建了一个set结构体。var skip map[string]struct{}if length := len(notlogged); length > 0 {skip = make(map[string]struct{}, length)for _, path := range notlogged {skip[path] = struct{}{}}}// 正文开始return func(c *Context) {// Start timerstart := time.Now()path := c.Request.URL.Pathraw := c.Request.URL.RawQuery// Process request```// 可能很多人会好奇为什么在中间件的中间还要执行一个next// 这是一个中间件在逻辑函数执行之前还有逻辑函数执行之后,都有钩子逻辑可以执行// 大家可以把这个想象成一个套娃的结构// 例如:logger() 前半段logic() 业务逻辑代码logger() 后半段```c.Next()// Log only when path is not being skipped// 上面英文很简单大家自己看看if _, ok := skip[path]; !ok {param := LogFormatterParams{Request: c.Request,isTerm: isTerm,Keys: c.Keys,}// Stop timer// 结构化打印,然后输出到流中,没啥好说的param.TimeStamp = time.Now()param.Latency = param.TimeStamp.Sub(start)param.ClientIP = c.ClientIP()param.Method = c.Request.Methodparam.StatusCode = c.Writer.Status()param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()param.BodySize = c.Writer.Size()if raw != "" {path = path + "?" + raw}param.Path = path// 输出完了fmt.Fprint(out, formatter(param))}}
}
不过一般大家的项目之中都不会用gin提供的logger中间件,因为大家都有自己的日志格式,不过你实在想用也可以,把自己的logger实例实现gin.logger的接口,然后在初始化engine的时候传给engine就好了
完
相关文章:
go web框架 gin-gonic源码解读03————middleware
go web框架 gin-gonic源码解读03————middleware(context) 今天打完游戏有空整理整理之前看的gin的中间件设计,go的中间件设计相较于前两站还是蛮简单,蛮容易看懂的,所以顺便把context也一起写一下。 中间件是现在w…...
win10电脑记事本在哪里?电脑记事本如何查看字数?
在日常工作中,我们会遇到许多需要记录的信息和事项,而使用电脑记事本工具可以帮助我们方便地保存、管理这些内容。无论是记录工作会议的要点、制定工作计划,还是记录灵感和创意,电脑记事本都是非常实用的工具。 那么win10电脑记事…...
【微服务】06-安全问题
文章目录 1.反跨站请求伪造1.1 攻击过程1.2 攻击核心1.3 如何防御1.4 使用AntiforgeryToken机制来防御用到的类 2. 防开发重定向共计2.1 攻击过程2.2 攻击核心2.3 防范措施 3.防跨站脚本3.1 攻击过程3.2 防范措施 4.跨域请求4.1 同源与跨域4.2 CORS过程4.2 CORS是什么4.3 CORS请…...
js的this指向问题
代码一: 这段代码定义了run函数、obj对象,然后我们把run函数作为obj的方法。 function run(){console.log(this);}let obj{a:1,b:2};obj.runrun;obj.run(); 那么我们调用obj的run方法,那么这个方法打印的this指向obj。 分析:即…...
Redis常用数据类型及命令
Redis 常用数据类型 常用数据类型 主要是指value类型 key都是字符串类型的 各种数据类型对应的特点 应用场景 哈希:一般来存储一些对象 列表:存一些跟顺序有关系的数据,比如朋友圈点赞 集合:一般用来做运算,交集&a…...
软件工程(六) 面向对象分析(OOA)之UML图特点
1、UML 面向对象分析里面有一个非常重要的工具叫UML,UML不仅在工作中非常重要,在考试当中也是非常重要的,即作为上午综合体,又大概率又会出现在下午的案例分析中,作为一个25分的大题。 UML叫做统一建模语言,它主要用于需求分析和软件的设计,来做一些模型的制作。比如我…...
QT 消息对话框按钮显示
前言 搞QT嘛,大多数都是军工。都要国产化,而且消息对话框的按钮的英文也不是很得劲,所以需要汉化。使用静态函数的按钮就是显示英文,汉化的代码如下。 void Widget::on_pushButton_clicked() {QMessageBox box(QMessageBox::Inf…...
平衡二叉树及其应用详解
平衡二叉树 定义与性质 平衡二叉树(Balanced Binary Tree)是计算机科学中的一种数据结构,它是二叉排序树的一种特殊情况。 平衡二叉树满足以下性质: 左子树和右子树的高度差不超过 1。也就是说,对于任意节点&#…...
vue3+ ts ts语法在script写不知道为啥一直报错
在vue3页面中写ts语法 发现识别不了 一直报错 1.出现这种问题的话,首先查看自己写的有没有问题,没有问题的话 2.再查看 script里边有没有写 lang"ts" <script setup lang"ts">解析 setup:是vue3在单文件组件 (SFC) 中使用 composition …...
c#写的端口监听,程序退出后,再次运行提示端口占用,且进程不存在
我用c#写了一个监听29999端口,进程结束后再次启动发现端口被占用,但是运行netstat -ano | findstr 29999找到进程ID后,却没有这个进程 经查询这个监听29999进程虽然没了,但是要找到他的父进程,把父进程关闭了才可以,参…...
跨域案例go gf ,请求代理,前端请求后端A转发给多个后端B
跨域案例go gf ,请求代理,前端请求后端A转后端B 案例:从前端请求后端A(路径携带argusx),后端A转发请求到多个不同地区(可一个)后端B(切掉argusx,其他不变进行请求)&…...
9.4 集成功率放大电路
OTL、OCL 和 BTL 电路均有各种不同输出功率和不同电压增益的集成电路。应当注意,在使用 OTL 电路时,需外接输出电容。为了改善频率特性,减小非线性失真,很多电路内部还引入深度负反馈。这里以低频功放为例。 一、集成功率放大电路…...
Java“牵手“拼多多商品详情数据、拼多多优惠券信息、拼多多到手价信息获取方法,拼多多API实现批量商品数据抓取示例
拼多多商城是一个网上购物平台,售卖各类商品,包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取拼多多商品详情数据,您可以通过开放平台的接口或者直接访问拼多多商城的网页来获取商品详情信息。以下是两种常用方法的介绍:…...
亚马逊云科技 re:Inforce 大会云安全合规与技术实践及 Security Jam 大赛,快来报名吧!...
2023年8月31日在北京 亚马逊云科技 re:Inforce 大会 首次登陆中国! 我们期待您的莅临, 并与您一起迎接 AI 时代, 开启全面智能的安全旅程! 在13:00-17:00的 培训与动手实验环节中 云安全合规与技术实践 及 Security Jam 大赛…...
网络安全(黑客技术)学习手册
1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高; 二、则是发展相对成熟…...
泡泡玛特回应头部IP营收增速放缓:IP上市时间不固定
8月23日,针对今年上半年头部IP营收增速放缓问题,泡泡玛特(09992.HK)管理层在业绩会上解释称,每个IP上市时间并不固定,单从上半年看同比增长会有偏差,而随着下半年两个新系列的推出,全…...
很干的 Nginx
🎨 前言 本篇文章有些概念性的东西,是结合自己的理解表达出来的,可能有些理解不到位的地方。希望多多指教,谢谢大家。 红包献上 🧧🧧🧧🧧🧧🧧🧧…...
【已解决】pycharm突然双击无法打开,重启电脑也不管用
1.问题: pycharm突然双击无法打开,重启电脑也不管用 2.解决 2.1 方法一(修改Roaming) 1.找到C盘对应路径下的pycharm版本 2. 用记事本打开文件类型为VMOPTIONS文件 3. 修改或删除最后一行的映射路径 4.保存退出 2.2 方法二…...
【HCIP】15.MPLS基础
多协议标签交换 MPLS位于TCP/IP协议栈中的数据链路层和网络层之间,可以向所有网络层提供服务。 通过在数据链路层和网络层之间增加额外的MPLS头部,基于MPLS头部实现数据快速转发。 术语 MPLS域(MPLS Domain):一系列…...
热烈祝贺重庆融能成功入选航天系统采购供应商库
经过航天系统采购平台的严审,重庆融能机电设备股份有限公司成功入选中国航天系统采购供应商库。航天系统采购平台是航天系统内企业采购专用平台,服务航天全球范围千亿采购需求,目前,已有华为、三一重工、格力电器、科大讯飞等企业…...
打破平台壁垒:Windows上安装APK文件的完整解决方案
打破平台壁垒:Windows上安装APK文件的完整解决方案 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否曾想过在Windows电脑上直接运行安卓应用ÿ…...
多模态AI实战:基于OpenGVLab/Ask-Anything构建视觉问答系统
1. 项目概述:当视觉大模型学会“看图说话”最近在折腾多模态AI应用,发现了一个挺有意思的开源项目,叫OpenGVLab/Ask-Anything。简单来说,它就像一个给AI装上了“眼睛”和“嘴巴”的系统,你给它一张图片或一段视频&…...
VS Code光标主题定制指南:提升开发效率与视觉舒适度
1. 项目概述:一个为开发者量身定制的光标主题集合如果你和我一样,每天有超过8个小时的时间是在代码编辑器里度过的,那么你一定对那个在屏幕上闪烁的光标再熟悉不过了。它不仅仅是文本插入点,更是我们思维在数字世界中的延伸。然而…...
终极指南:如何为你的Mac鼠标安装强大定制功能
终极指南:如何为你的Mac鼠标安装强大定制功能 【免费下载链接】mac-mouse-fix Mac Mouse Fix - Make Your $10 Mouse Better Than an Apple Trackpad! 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix Mac Mouse Fix是一款革命性的开源工具…...
【仅限前200名】Midjourney铂金印相专属Prompt库泄露:含17组经暗房验证的--v 6.2参数矩阵与胶片光谱校准模板
更多请点击: https://intelliparadigm.com 第一章:Midjourney铂金印相的光学本质与历史语境 铂金印相(Platinum Print)并非数字时代的产物,而是一种诞生于1873年的古典摄影工艺——其影像由铂族金属(主要是…...
构建个人技能库:用GitHub+Markdown打造开发者的第二大脑
1. 项目概述:从“我的Copaw技能”看个人技能库的构建与管理最近在GitHub上看到一个挺有意思的项目,叫“my-copaw-skill”。这个项目名本身就很有故事感,“Copaw”这个词,我猜是“Code”和“Paw”(爪子)的结…...
服务网格Istio实战
服务网格Istio实战 引言 服务网格(Service Mesh)作为微服务架构的基础设施层,提供了对服务间通信的精细控制能力。Istio是目前最流行的开源服务网格解决方案,它通过Sidecar代理拦截所有网络通信,提供流量管理、安全、可…...
自建轻量级Docker镜像中心:聚合管理与加速部署实践
1. 项目概述:一个面向容器化开发者的中心化镜像仓库最近在和一些做容器化开发的朋友交流时,大家普遍提到一个痛点:随着团队项目增多,Docker镜像的管理变得越来越零散。有的镜像放在Docker Hub,有的放在阿里云镜像服务&…...
OPAL:基于OPA的实时策略数据分发与权限治理实践
1. 项目概述:什么是OPAL,以及它解决了什么核心痛点?如果你在负责一个微服务架构或者分布式系统的权限管理,大概率遇到过这样的场景:每次权限策略有更新,都需要重启服务、重新部署,或者等待一个漫…...
湿版摄影风格失效的5个致命误区,第4个连Midjourney官方文档都未披露——基于217组AB测试的权威归因报告
更多请点击: https://intelliparadigm.com 第一章:湿版摄影风格失效的5个致命误区,第4个连Midjourney官方文档都未披露——基于217组AB测试的权威归因报告 为何“wet plate collodion”提示词突然失灵? 在 Midjourney v6.1 及 N…...
