【Golang】gin框架如何在中间件中捕获响应并修改后返回

【Golang】gin框架如何在中间件中捕获响应并修改后返回

  • 本文讲述如何捕获中间件响应以及重写响应
    • 如果想在中间件中记录响应日志等操作,我们该如何获取响应数据呢?
    • 假如需要统一对响应数据做加密,如何修改这个返回数据再响应给客户端呢?
    • 参考

本文讲述如何捕获中间件响应以及重写响应

在gin框架中,在控制器里面调用c.JSON(code, jsonObj)后,向HTTP响应中写入JSON格式的数据,并且设置相应的HTTP状态码。当这个函数被调用时,数据并不会被“保存”到某个特定的位置,而是被直接写入到HTTP响应体中,并通过网络发送给客户端。

如果想在中间件中记录响应日志等操作,我们该如何获取响应数据呢?

package mainimport ("bytes" // 引入bytes包,用于处理字节缓冲区,帮助我们缓存响应体内容"net/http""github.com/gin-gonic/gin" // 导入Gin框架包
)// 定义一个responseWriterWrapper类型,用于包裹gin.ResponseWriter,以扩展其功能
type responseWriterWrapper struct {gin.ResponseWriter // 继承gin.ResponseWriter,保留原有功能body       *bytes.Buffer // 新增一个缓冲区,用于存储响应体的内容statusCode int           // 用于记录响应的状态码
}// 重写WriteHeader方法,用于在响应头被写入之前记录状态码
func (w *responseWriterWrapper) WriteHeader(statusCode int) {w.statusCode = statusCode // 记录状态码w.ResponseWriter.WriteHeader(statusCode) // 调用原始的WriteHeader方法发送状态码
}// 重写Write方法,实现在响应体内容被写入时同时缓存这些内容
func (w *responseWriterWrapper) Write(b []byte) (int, error) {w.body.Write(b) // 将响应体内容写入缓冲区进行缓存return w.ResponseWriter.Write(b) // 调用原始的Write方法将内容写入实际的响应体
}// 定义loggingMiddleware中间件,用于在每个请求结束时打印响应的状态码和内容
func loggingMiddleware(c *gin.Context) {// 创建一个responseWriterWrapper实例,用于替换当前的ResponseWriterwriter := &responseWriterWrapper{ResponseWriter: c.Writer, // 使用原ResponseWriter初始化body:           &bytes.Buffer{}, // 初始化一个空的缓冲区}c.Writer = writer // 将上下文中的Writer替换为我们自定义的writer// 继续执行后续的请求处理链c.Next()// 在所有的处理完成后,可以从writer中获取并打印响应的状态码和内容status := writer.statusCodebody := writer.bodyprintln("Response Status:", status) // 打印状态码println("Response Body:", body.String()) // 将缓冲区内容转换为字符串并打印
}func main() {// 初始化Gin引擎,默认使用Logger和Recovery中间件r := gin.Default()// 使用我们自定义的loggingMiddleware中间件r.Use(loggingMiddleware)// 定义一个简单的路由,返回JSON响应r.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Hello, world!"}) // 返回成功状态码和一条消息})// 启动服务器,监听0.0.0.0:8080r.Run()
}

假如需要统一对响应数据做加密,如何修改这个返回数据再响应给客户端呢?

package mainimport ("bytes"         // 引入字节缓冲区处理包,用于缓存响应体"encoding/json" // 引入JSON编码解码包,用于处理JSON数据"net/http""github.com/gin-gonic/gin" // 导入Gin框架包
)// 定义responseWriterWrapper结构体,用于封装gin.ResponseWriter并添加缓冲区以存储响应体内容
type responseWriterWrapper struct {gin.ResponseWriter               // 继承gin.ResponseWriter接口body               *bytes.Buffer // 使用字节缓冲区存储响应体
}// 重写Write方法,将响应体内容写入缓冲区
func (w *responseWriterWrapper) Write(b []byte) (int, error) {return w.body.Write(b)
}// encryptMiddleware 是自定义中间件,用于在响应发送前进行日志记录或数据处理(例如加密)
func encryptMiddleware(c *gin.Context) {// 创建responseWriterWrapper实例,替换默认的ResponseWriterw := &responseWriterWrapper{ResponseWriter: c.Writer,body:           &bytes.Buffer{},}c.Writer = w// 标记,指示是否已经对响应数据进行了加密处理isEncrypt := false// 使用defer确保无论函数如何退出都能重置缓冲区并最终写出响应defer func() {if !isEncrypt {// 如果没有加密,则直接将缓存的内容写出w.ResponseWriter.Write(w.body.Bytes())}w.body.Reset() // 重置缓冲区以备后续请求使用}()// 继续执行后续的处理链,这里是重复调用了c.Next(),在实际应用中应避免,这里为了示例简化处理c.Next()// 解析缓冲区中的JSON数据到gin.H类型变量result中var result gin.Hif err := json.Unmarshal(w.body.Bytes(), &result); err != nil {return // 如果解析出错,直接返回不作处理}// 检查响应中是否存在code字段,并判断其值是否为0codeValue, ok := result["code"].(float64) // JSON解码时int可能转为float64if !ok || int(codeValue) != 0 {return // 如果code不是预期值,则不进行加密处理}// 获取响应中的"data"字段dataValue, ok := result["data"]if !ok {return // 如果"data"不存在,则不进行处理}// 加密逻辑,这里仅为示例,实际加密过程应替换此简单字符串替换逻辑encryptFunc := func(data any) string {return "我是加密后字符串"}encryptedData := encryptFunc(dataValue)// 修改响应体中的"data"为加密后的数据,并增加"is_encrypt"字段result["data"] = encryptedDataresult["is_encrypt"] = trueisEncrypt = true // 设置标记表示已加密// 将修改后的结果重新序列化为JSON格式newBody, err := json.Marshal(result)if err != nil {// 序列化出错则取消加密标记,避免写出错误数据isEncrypt = falsereturn}// 将加密后的新响应体写回客户端_, _ = w.ResponseWriter.Write(newBody)
}func main() {// 初始化Gin路由器,并使用自定义中间件r := gin.Default()r.Use(encryptMiddleware)// 定义一个GET路由,返回JSON响应r.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"code": 0, "message": "Hello, world!", "data": "..."})})// 启动HTTP服务器r.Run() // 监听0.0.0.0:8080并开始服务
}

执行:curl "http:/127.0.0.1:8080/" 返回

{"code": 0,"data": "我是加密后字符串","is_encrypt": true,"message": "Hello, world!"
}

参考

[1]: How to rewrite response body in middleware? #3384

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

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

相关文章

适合学生党的蓝牙耳机有哪些?盘点四大性价比蓝牙耳机品牌

对于追求高品质音乐体验而又预算有限的学生党来说,一款性价比高的蓝牙耳机无疑是最佳选择,在众多品牌和型号中,如何挑选到既适合自己需求又价格亲民的蓝牙耳机,确实是一个值得思考的问题,作为一个蓝牙耳机大户&#xf…

AI 绘图要如何入门?有哪些好用的软件推荐?(附工具+教程+变现模式)

1.Ai绘画如何入门 不需要把Ai绘画想的很复杂 抛去复杂的应用 使用现成简单的工具 只需要学会提示词 描述你想要的画面即可 提示词 不需要太复杂,也不能太简单,太简单依赖于ai的基本样式,关键是要抓住你想要的核心描述 AI不太擅长理解人类的…

leetcode 712.两个字符串的最小ASCII删除和

思路:LCS,dp 其实就是把dp的状态信息变了一下,但是本质上的状态转移其实是没有很大的变动,既然是让我们删除其他不一样的字符,那么保留下来的肯定就是两个字符串的最长公共子序列了。这样的我们就可以设状态方程为最长…

k8s笔记——GVK是什么

文章目录 k8s的GVK是什么查看资源的GVK查看pod查看service查看Deployment查看NameSpace查看Node 参考资料 k8s的GVK是什么 在 Kubernetes 中,GVK 是指 Group、Version 和 Kind 三个字段,用于唯一标识 Kubernetes 资源对象。 Group 指的是 Kubernetes A…

如何批量结构化分汇多工作表sheet?

目录 一、如遇合并表格,注意结构化二、确认主键,合并所有文件数据三、sheet2同理四、案例总结 如果遇到这样情形,多文件夹多文件,多工作表的分汇场景;可以参考以下方法解决。 一、如遇合并表格,注意结构…

脑机接口:是现代医学的外挂,更是瘫痪病人的豪赌

5 月 17 日,马斯克公开表示,继今年年初首次成功将大脑芯片植入患者大脑后,Neuralink 正在寻找第二位受试者接受这项手术。 5 月 20 日,美国食品药品监督管理局 (FDA) 批准了马斯克的 Neuralink 公司为第二位患者植入脑芯片&#…

Python图形界面(GUI)Tkinter笔记(十四):Entry与Button的碰撞(1)

用功能按钮(Button)、单行文本输入框(Entry)、文本框内容读取(get)实现一个极简易的加法运算,及与其他控件的交互,提高体验,主要体现其人机交互的意义。因为Entry()文本输入框没有限制输入内容属性的参数,它是把所有的输入都视作它特有的一个类属性,所以用get()方法读取出…

若依新增页面,在左侧显示菜单栏的页面,可点击

选择指定的某个目录下 菜单名称,路由地址,组件路径这几个是必填的,其他的暂时就不用管了。 菜单名称:就是显示到左侧目录中的名称。 路由地址:自定义,一般写页面名称就可以。 组件路径:根据前端…

PDF打印技巧:如何跳过不需要的页面?如何关闭打印权限?

作为打工人,经常需要打印各种文档,比如PDF文件。今天分享一下PDF文件的两个打印技巧,如果你还不知道,就一起来看看吧! 技巧1:打印PDF如何跳过不需要的页面 有时候,一个PDF文件有很多页&#xf…

基于GIS地理信息技术的智慧巡检平台建设方案(Word原件)

传统的巡检采取人工记录的方式,该工作模式在生产中存在很大弊端,可能造成巡检不到位、操作失误、观察不仔细、历史问题难以追溯等现象,使得巡检数据不准确,设备故障隐患得不到及时发现和处理。因此建立一套完善的巡检管理系统是企…

DiffIR论文阅读笔记

ICCV2023的一篇用diffusion模型做Image Restoration的论文,一作是清华的教授,还在NIPS2023上一作发表了Hierarchical Integration Diffusion Model for Realistic Image Deblurring,作者里甚至有Luc Van Gool大佬。模型分三个部分&#xff0c…

ChatGPT实现法语口语练习APP

使用ChatGPT实现一个法语口语练习APP可以提供一个强大的工具,帮助学习者提高他们的口语能力。以下是一个详细的实现流程,包括需求分析、技术选型、开发、测试和部署。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合…

VUE3 学习笔记(7):如何简单的理解VUE 组件,并手把手实现一个嵌套组件

基本概念 VUE 最大的特点就是组件化&#xff0c;理解组件化可视为积木模块&#xff0c;其特点就是增加了复用性。把一个个.vue文件就是组件&#xff08;又作SFC&#xff09;&#xff0c;组件的组合就是一个功能页。 组成部分说明 <!--内容控制&#xff08;必要&#xff09;…

Ubuntu 离线下载安装 Tmux(亲测有效)

昨晚跑NER模型中断了&#xff0c;今天就考虑安装下Tmux&#xff0c;但是一直安装不上&#xff0c;在尝试了好几次之后&#xff0c;终于不报错了&#xff01;&#xff01;特记录一下下载安装过程。&#xff08;我这里是离线下载安装的&#xff09; 1. 下载安装包 tmux wget ht…

微信小程序上架,AI类目审核(AI问答、AI绘画、AI换脸)

小程序对于生成式AI类目的产品上架审核较为严格&#xff0c;这也是近两年新增了几个类目&#xff0c;一旦小程序中涉及生成式AI相关的内容&#xff0c;如果你选择相应类目&#xff0c;但审核被划归为这一类&#xff0c;都需要准备此类目的审核&#xff0c;才能正常上架。 如果…

【杂记-浅谈DDos攻击、浅析SYN Flood攻击、Dos及DDos攻击区别】

一、DDos Distributed Denial of Service 分布式拒绝服务攻击 什么是DDos攻击 DDoS攻击是一种常见的网络攻击形&#xff0c;攻击者利用恶意程序对一个或多个目标发起攻击&#xff0c;企图通过大规模互联网流量耗尽攻击目标的网络资源&#xff0c;使目标系统无法进行网络连接、…

MT3049 区间按位与

思路&#xff1a; 使用ST表。ST表模板可参考MT3024 maxmin 注意点&#xff1a;此题范围较大&#xff0c;所以要避免超时。 ①使用 ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); 加快输入输出速度。 ②换行使用\n而不是endl 代码&#xff1a; 1.暴力6/8 #…

YOLOv10最详细全面讲解2- 目标检测-环境搭建、训练自己的数据集

YOLOv10没想到出来的如此之快&#xff0c;作为一名YOLO的爱好者&#xff0c;以YOLOv5和YOLOv8的经验&#xff0c;打算出一套从数据集装备->环境配置->训练->验证->目标追踪全系列教程。请大家多多点赞和收藏&#xff01;&#xff01;&#xff01; 系列文章&#xf…

睿联技术对亚马逊既依赖又竞争:递表前大额分红,资金充裕又补流?

《港湾商业观察》施子夫 王璐 今年3月29日&#xff0c;冲刺创业板IPO的深圳市睿联技术股份有限公司&#xff08;以下简称&#xff0c;睿联技术&#xff09;提交了注册&#xff0c;不出意外的话&#xff0c;公司离挂牌上市已经近在咫尺。 然而&#xff0c;在目前资本市场尤其…

asammdf 运行报错 “ldf is not supported”导致打不开页面解决办法

asammdf简介 asammdf 是一个用于处理和分析测量数据格式&#xff08;MDF&#xff09;文件的 Python 库。MDF 文件通常用于汽车行业&#xff0c;记录车辆中各种传感器和控制单元的数据。asammdf 提供了读取、写入和操作这些文件的工具&#xff0c;能够高效地处理大量数据。 as…