Gin 优雅打印请求与回包内容
文章目录
- 1.Gin 的 Middleware
- 2.使用 Middleware 打印请求与回包内容
- 3.多次读取请求 Body 的问题
- 4.多次读取响应 Body 的问题
- 5.小结
- 参考文献
在开发 Web 应用程序时,难免不会遇到功能或性能等问题。为了快速定位问题,需要打印请求和响应的内容。
本文将介绍如何使用 Gin 框架来优雅地打印请求和响应的内容。
1.Gin 的 Middleware
在 Gin 框架中,中间件是一种用于拦截 HTTP 请求和响应的机制。中间件函数可以在请求到达处理程序之前或之后执行某些操作,例如打印请求和响应的内容、验证请求数据等。
Gin 框架提供了一种简单的方法来定义和使用中间件。中间件函数需要满足以下条件:
- 函数的签名必须是 func(c *gin.Context),其中 c 是 Gin 框架中的上下文对象。
- 函数可以执行任何操作,但是必须调用 c.Next() 方法来继续执行请求处理程序和其他中间件函数。
- 如果需要在请求处理程序之后执行某些操作,可以在调用 c.Next() 之后执行。
2.使用 Middleware 打印请求与回包内容
下面是一个使用 Gin 中间件来打印请求和响应内容的示例代码:
func Logger() gin.HandlerFunc {return func(c *gin.Context) {// 记录请求时间start := time.Now()// 打印请求信息reqBody, _ := c.GetRawData()fmt.Printf("[INFO] Request: %s %s %s\n", c.Request.Method, c.Request.RequestURI, reqBody)// 执行请求处理程序和其他中间件函数c.Next()// 记录回包内容和处理时间end := time.Now()latency := end.Sub(start)respBody := string(c.Writer.Body.Bytes())fmt.Printf("[INFO] Response: %s %s %s (%v)\n", c.Request.Method, c.Request.RequestURI, respBody, latency)}
}func main() {r := gin.Default()// 注册中间件r.Use(Logger())r.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "Hello, World!")})r.Run(":8080")
}
上面的代码定义了一个中间件,用来记录请求和回包内容。在中间件中,我们首先记录请求的时间和请求内容,然后调用 c.Next() 继续处理请求。在请求处理完成后,我们记录回包内容和处理时间。最后,我们使用 gin.Default() 函数来创建一个 Gin 引擎实例,并注册路由和中间件。启动服务后,我们可以访问http://localhost:8080/hello
查看请求和回包的内容。
3.多次读取请求 Body 的问题
实际上,上面的做法会有问题。
在中间件中读取了请求的 Body,如果在接口处理函数中再次读取 Body,会导致 Body 被读取两次,从而出现问题。 因为在读取 Body 后,Body 的指针会被移到末尾,第二次读取时就无法再次读取到内容。
那么 Gin 如何正确多次读取 http request body 的内容呢?
解决思路: 由于 Request.Body 为公共变量,我们在对原有的 buffer 读取完成后,只要手动创建一个新的 buffer 然后以同样接口形式替换掉原有的 Request.Body 即可。
reqBytes, _ := c.GetRawData()// 请求包体写回。
if len(reqBytes) > 0 {c.Request.Body = io.NopCloser(bytes.NewBuffer(reqBytes))
}
在 Go 语言中,io.NopCloser 函数返回一个实现 io.ReadCloser 接口的对象,这个对象可以包装任何实现 io.Reader 接口的对象,并提供了一个空的 Close 方法。这个方法的作用就是什么也不做,仅仅是返回一个 nil 的 error。
通常情况下,我们会使用 io.ReadCloser 接口读取数据,并在读取完成后关闭相关资源,例如打开的文件句柄或者网络连接等。但是在某些场景下,我们希望读取数据,但是并不想关闭相关资源,比如在数据读取完成后还需要进行一些其他操作,或者需要多次读取同一个资源等。
这时候,就可以使用 io.NopCloser 函数将一个实现了 io.Reader 接口的对象包装成一个实现了 io.ReadCloser 接口的对象,并在 Close 方法中什么也不做。这样就可以在读取数据后不关闭相关资源,从而方便进行其他操作或者多次读取同一个资源。
4.多次读取响应 Body 的问题
同样地,在中间件中读取响应 Body 的问题是,它会使得缓冲区被读取完毕,指针指向了缓冲区的末尾,而后续的代码再次读取 Body 时,指针已经到了缓冲区的末尾,无法再次读取。
为了避免这个问题,我们可以使用一个自定义的 ResponseWriter 来替换 Gin 默认的 ResponseWriter。自定义的 ResponseWriter 可以将响应 Body 写入到一个内存缓冲区中,并在中间件中获取响应 Body 并记录日志。
下面是一个完整的可以在日志中间件中读取请求与响应 Body 的示例。
// CustomResponseWriter 封装 gin ResponseWriter 用于获取回包内容。
type CustomResponseWriter struct {gin.ResponseWriterbody *bytes.Buffer
}func (w CustomResponseWriter) Write(b []byte) (int, error) {w.body.Write(b)return w.ResponseWriter.Write(b)
}// 日志中间件。
func Logger() gin.HandlerFunc {return func(c *gin.Context) {// 记录请求时间start := time.Now()// 使用自定义 ResponseWritercrw := &CustomResponseWriter{body: bytes.NewBufferString(""),ResponseWriter: c.Writer,}c.Writer = crw// 打印请求信息reqBody, _ := c.GetRawData()fmt.Printf("[INFO] Request: %s %s %s\n", c.Request.Method, c.Request.RequestURI, reqBody)// 执行请求处理程序和其他中间件函数c.Next()// 记录回包内容和处理时间end := time.Now()latency := end.Sub(start)respBody := string(crw.body.Bytes())fmt.Printf("[INFO] Response: %s %s %s (%v)\n", c.Request.Method, c.Request.RequestURI, respBody, latency)}
}
5.小结
在本文中,我们介绍了为什么要打印请求与回包内容,以及如何使用 Gin 的 Middleware 功能来打印请求和回包内容。通过打印请求和回包内容,我们可以更好地了解 API 的执行过程,并且可以快速定位问题。
参考文献
OpenAI ChatGPT
Using middleware | Gin Web Framework
如何让gin正确多次读取http request body内容- 掘金
如何让gin 正确读取http response body 内容,并多次使用- 掘金
相关文章:
Gin 优雅打印请求与回包内容
文章目录1.Gin 的 Middleware2.使用 Middleware 打印请求与回包内容3.多次读取请求 Body 的问题4.多次读取响应 Body 的问题5.小结参考文献在开发 Web 应用程序时,难免不会遇到功能或性能等问题。为了快速定位问题,需要打印请求和响应的内容。本文将介绍…...
关于k8s中ETCD集群备份灾难恢复的一些笔记
写在前面 集群电源不稳定,或者节点动不动就 宕机,一定要做好备份,ETCD 的快照文件很容易受影响损坏。重置了很多次集群,才认识到备份的重要博文内容涉及 etcd 运维基础知识了解静态 Pod 方式 etcd 集群灾备与恢复 Demo定时备份的任务编写二进…...
【设计模式之美 设计原则与思想:设计原则】19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
关于 SOLID 原则,我们已经学过单一职责、开闭、里式替换、接口隔离这四个原则。今天,我们再来学习最后一个原则:依赖反转原则。在前面几节课中,我们讲到,单一职责原则和开闭原则的原理比较简单,但是&#x…...
2023年全国最新高校辅导员精选真题及答案13
百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 一、单选题 131.下列不属于我国国土空间具有的特点的是() A.水资…...

【XXL-JOB】XXL-JOB定时处理视频转码
【XXL-JOB】XXL-JOB定时处理视频转码 文章目录【XXL-JOB】XXL-JOB定时处理视频转码1. 准备工作1.1 高级配置1.2 分片广播2. 需求分析2.1 作业分片方案2.2 保证任务不重复执行2.2.1 保证幂等性3. 视频处理业务流程3.1 添加待处理任务3.2 查询待处理任务3.3 更新任务状态3.4 工具…...
optuna用于pytorch的轻量级调参场景和grid search的自定义设计
文章目录0. 背景:why optuna0.1 插播一个简单的grid search0.2 参考1. Optuna1.1 a basic demo与部分参数释义1.2 random的问题1.3 Objective方法类2. Optuna与grid search4. optuna的剪枝prune5. optuna与可视化6. 未完待续0. 背景:why optuna 小模型参…...

语法篇--汇编语言先导浅尝
一、相关概念 1.机器语言 机器语言(Machine Language)是一种计算机程序语言,由二进制代码(0和1)组成,可被计算机直接执行。机器语言是计算机硬件能够理解和执行的唯一语言。 机器语言通常由一系列的指令组…...

【ID:17】【20分】A. DS顺序表--类实现
时间限制1秒内存限制128兆字节题目描述用C语言和类实现顺序表属性包括:数组、实际长度、最大长度(设定为1000)操作包括:创建、插入、删除、查找类定义参考输入第1行先输入n表示有n个数据,即n是实际长度;接着输入n个数据…...

【java web篇】Tomcat的基本使用
📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言Ὅ…...

MySQL实战解析底层---行锁功过:怎么减少行锁对性能的影响
目录 前言 从两阶段锁说起 死锁和死锁检测 前言 MySQL 的行锁是在引擎层由各个引擎自己实现的但并不是所有的引擎都支持行锁,比如MyISAM 引擎就不支持行锁不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有…...

初识STM32单片机
目录 初识STM32单片机 什么是单片机? STM系列单片机命名规则 STM32F103C8T6单片机简介 标准库与HAL库区别 通用输入输出端口GPIO 什么是GPIO? 定义 命名规则 内部框架图 推挽输出与开漏输出 如何点亮一颗LED灯 编程实现点灯 按键点亮LED灯…...

数据结构与算法系列之单链表
💗 💗 博客:小怡同学 💗 💗 个人简介:编程小萌新 💗 💗 如果博客对大家有用的话,请点赞关注再收藏 🌞 这里写目录标题test.hSList.h注意事项一级指针与二级指针的使用assert的使用空…...
MySQL基础
本单元目标 一、为什么要学习数据库 二、数据库的相关概念 DBMS、DB、SQL 三、数据库存储数据的特点 四、初始MySQL MySQL产品的介绍 MySQL产品的安装 ★ MySQL服务的启动和停止 ★ MySQL服务的登录和退出 ★ MySQL的常见命令和语法规范 五、…...

面试热点题:环形链表及环形链表寻找环入口结点问题
环形链表 问题: 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接…...

【算法】DFS与BFS
作者:指针不指南吗 专栏:算法篇 🐾题目的模拟很重要!!🐾 文章目录1.区别2.DFS2.1 排列数字2.2 n-皇后问题3.BFS3.1走迷宫1.区别 搜索类型数据结构空间用途过程DFSstackO( n )不能用于最短路搜索到最深处&a…...

湖州银行冲刺A股上市:计划募资约24亿元,资产质量水平较高
3月4日,湖州银行股份有限公司(下称“湖州银行”)递交招股书,准备在上海证券交易所主板上市。本次冲刺上市,湖州银行计划募资23.98亿元,将在扣除发行费用后全部用于补充该行资本金。 湖州银行在招股书中表示…...

高性能网络I/O框架-netmap源码分析
前几天听一个朋友提到这个netmap,看了它的介绍和设计,确实是个好东西。其设计思想与业界不谋而合——因为为了提高性能,几个性能瓶颈放在那里,解决方法自然也是类似的。 netmap的出现,它既实现了一个高性能的网络I/O框…...

SpringBoot监听机制-以及使用
11-SpringBoot事件监听 Java中的事件监听机制定义了以下几个角色: ①事件:Event,继承 java.util.EventObject 类的对象 ②事件源:Source ,任意对象Object ③监听器:Listener,实现 java.util…...

若依学习——定时任务代码逻辑 详细梳理(springboot整合Quartz)
springboot整合Quartz关于若依定时任务的使用可以去看视频默认定时任务的使用关于springboot整合quartz的整合参考(150条消息) 定时任务框架Quartz-(一)Quartz入门与Demo搭建_quarzt_是Guava不是瓜娃的博客-CSDN博客(150条消息) SpringBoot整合Quartz_springboot quartz_桐花思…...
C++---最长上升子序列模型---拦截导弹(每日一道算法2023.3.4)
注意事项: 本题为"线性dp—最长上升子序列的长度"的扩展题,这里只讲贪心思路,dp去这个看。 题目: 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷:虽然它…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...