页首Html代码

返回顶部

起因: 困惑

使用了go的http服务后, 发现 request.Body 居然只能读取一次,第二次读取数据为nil.

比如我在gin的服务器中, 先加入了accessLog,需要进行parseForm() 但是后续居然读不到数据.

所以打算深入分析一下,然后简单的解决下这个问题,再优化一下.

分析下源代码

request.Body的类型为: Body io.ReadCloser

	// Body is the request's body.
	//
	// For client requests, a nil body means the request has no                  	对于客户端请求,nil 正文表示请求没有
	// body, such as a GET request. The HTTP Client's Transport                  	正文,例如 GET 请求。HTTP 客户端的传输
	// is responsible for calling the Close method.                              	负责调用 Close 方法。
	//                                                                           	//
	// For server requests, the Request Body is always non-nil                   	对于服务器请求,请求正文始终为非 nil
	// but will return EOF immediately when no body is present.                  	但会在body没有提供数据时立即返回EOF。
	// The Server will close the request body. The ServeHTTP                     	服务器将关闭请求正文。The ServeHTTP
	// Handler does not need to.                                                 	处理程序不需要。
	//                                                                           	//
	// Body must allow Read to be called concurrently with Close.                	正文必须允许读取与关闭同时调用。
	// In particular, calling Close should unblock a Read waiting                	特别是,调用 Close 应取消阻止读取等待
	// for input.                                                                	用于输入。
	Body io.ReadCloser

可以看到 这就是一个 reader+closer 接口,真实的数据可以打印出来是:

log.Printf("c.Request.Body is %T", c.Request.Body)

结果为:
   c.Request.Body is *http.body

http.body是个未导出的类型

// body turns a Reader into a ReadCloser.
// Close ensures that the body has been fully read
// and then reads the trailer if necessary.
type body struct {
	src          io.Reader
	hdr          any           // non-nil (Response or Request) value means read trailer
	r            *bufio.Reader // underlying wire-format reader for the trailer
	closing      bool          // is the connection to be closed after reading body?
	doEarlyClose bool          // whether Close should stop early

	mu         sync.Mutex // guards following, and calls to Read and Close
	sawEOF     bool
	closed     bool
	earlyClose bool   // Close called and we didn't read to the end of src
	onHitEOF   func() // if non-nil, func to call when EOF is Read
}

好家伙 , 里面 src 又是一个 io.Reader 大部分又是 io.LimitReader ,算了不分析这个了, 无法直接进行 reset 的, 那就写一个新的reader 就好了.

纯Reader 是没有Reset或Seek接口的,这个真是应该把 request.Body 设置为 ReadSeekCloser 这种接口 ,那样就不会有此文问题了.

解决方案 (重点)

简单点的就是 读出一个 bytes 然后再覆盖掉 request.Body即可

func readBodyAndSetBodyRepeatRead(c *gin.Context, cb func()) {
	if s, ok := c.Request.Body.(io.Seeker); ok {
		//执行读取Body的操作
		cb()
		//再次设置可读状态
		_, err := s.Seek(0, 0)
		if err == nil {
			return
		}
	}

	bs, _ := io.ReadAll(c.Request.Body)
	_ = c.Request.Body.Close()// NOTE 原始的 Body 无需手动关闭,会在 response.reqBody中自动关闭的.
	//设置可读状态
	r := bytes.NewReader(bs)
	c.Request.Body = io.NopCloser(r)
	//执行读取Body的操作
	cb()
	//再次设置可读状态
	_, _ = r.Seek(0, 0)
}

bytes.NewReader 支持进行 Seek 设置,也就是可以重置读取指针(游标)位置,如果下次再次运行,就可以直接设置了

本想着 c.Request.Body 是不是要Close()呢? 查了下,发现可以不管,因为再 Response 结束后,会关闭的. 而且,这种写法也是安全的,可以看参考资料2

至于要不要用 pool 再次优化高并发下的性能,以减少 GC, 可以参考 参考资料2,我这里 就够用了.

用法

我是在gin下测试的,换做其他库简单修改下即可,反正request 都是原生 http.* 包下的.

	readBodyAndSetBodyRepeatRead(c, func() {
                io.ReadAll(c.Request.Body) // blabla
                 // or 
		_ = c.Request.ParseForm()
	})

参考资料:
1 本人学识
2 掘金网: 如何让 gin 正确多次读取 http request body 内容

posted @ 2023-03-08 11:47 ayanmw 阅读(1655) 评论(0) 推荐(0) 编辑
摘要: 判空 我们写程序都会有进行判空的操作: 对于map 对于golang的map,它为nil,你是无法直接使用的 如var oneMap map[uint32]uint32, 不赋值使用直接panic,必定要判空; 给它赋值为一个初始值var oneMap = map[uint32]uint32{} 使 阅读全文
posted @ 2025-02-28 19:39 ayanmw 阅读(2) 评论(0) 推荐(0) 编辑
摘要: json库的obmitempty介绍 众所周知,golang的json库 有个 omitempty的tag ,有了它,这个json序列化的时候,如果这个字段是零值,则会忽略此字段的序列化,导致json字符串中没有对应的字符串。 这对于某些人是困惑的,一般默认是没有 omitempty 这个tag的, 阅读全文
posted @ 2024-06-19 15:56 ayanmw 阅读(436) 评论(0) 推荐(0) 编辑
摘要: 起因 由于我所在项目 使用了 根据反射获取 Method 来注册RPC消息,但是我开发了一个模块,里面匿名组合了1个基础的结构,为了方便调用里面的方法,而不用每次都加一个中健变量 mgr.user.XXXX . 但是突然发现,所有的方法都执行了两遍,经过同事排查,终于发现是我匿名组合的问题了. 解决 阅读全文
posted @ 2024-06-13 10:58 ayanmw 阅读(52) 评论(0) 推荐(0) 编辑
摘要: golang 的内存占用是如何构成的呢? 变量本身内存 以及 变量指针指向区域的内存 加起来,如果有包含关系,则应该递归获取内存大小. unsafe.SizeOf() 一般可以获得变量本身的内存占用的,就用unsafe.SizeOf() 即可,可以获取基本类型: int int8,int16,int 阅读全文
posted @ 2024-05-06 14:12 ayanmw 阅读(560) 评论(0) 推荐(0) 编辑
摘要: WPS 云文档简介 具体使用参考下面链接 WPS系列课-WPS 云文档新手教程 WPS具有免费的1GB云空间,对于文档类来说,免费空间够用了,毕竟不跟 百度云 之类的通用云存储一样,WPS云 主要是以文档为主. 我可以在WPS云创建一个云文档, 我可以在电脑编辑完毕后, 用手机WPS 继续编辑. 这 阅读全文
posted @ 2024-04-10 13:07 ayanmw 阅读(1280) 评论(0) 推荐(0) 编辑
摘要: 看过 Nvidia 的ai-on-rtx的一个动画 AI 赋能游戏开发 加快游戏开发速度 无论是传统游戏还是新型游戏,现在都以各自的方式在融合 AI 技术。Modder 可以借助 RTX Remix 的革命性 AI 放大和纹理增强技术,让经典游戏焕发新活力。游戏开发者现在可以利用一种称为 Avata 阅读全文
posted @ 2024-03-21 10:53 ayanmw 阅读(42) 评论(0) 推荐(0) 编辑
摘要: 简介 一般容器都是特指基于linux的容器,其实windows也是有自己的容器系统镜像的,就是 nanoserver镜像 么,但是没有关于桌面windows 的容器镜像,倒是有一些server的镜像. 不过docker毕竟是docker,微软可以做WSL2,还可以开很多个实例, win10+有一个应 阅读全文
posted @ 2024-03-18 12:13 ayanmw 阅读(101) 评论(0) 推荐(0) 编辑
摘要: 现象 服务器pprof中的goroutines 很多,无法释放,肯定是异常. 代码 // 收到 请求上个赛季个人秘境赛季排行 func (this *MsgProc) MsgProc_PersonSecretLastRankReq(msg *protoMsg.PersonSecretLastRank 阅读全文
posted @ 2024-03-14 17:59 ayanmw 阅读(178) 评论(0) 推荐(0) 编辑
摘要: 什么时候开始? 根据 关于印发《个人养老金实施办法》的通知 是 发布时间:2022-11-04 有什么用? 参与个人养老金可以享受的个税优惠,老师已经整理在下面表格中了。对于收入较高的人群,最多可以退5400元。 除了个税优惠,目前貌似没有其他特别的好处,强制储蓄也算是吧,不过个人也可以强制各年固定 阅读全文
posted @ 2024-03-11 16:58 ayanmw 阅读(23) 评论(0) 推荐(0) 编辑
摘要: 需求 我做了一个排行榜,但是主键是pid,不是排名,排名作为唯一索引,两个人排名交换,只需要交换 排名唯一索引值即可. 但是直接单独更新 提示错误: Duplicate entry '3' for key 'priority_UNIQUE' 方法 本来希望可以在一条SQL语句中交换两个唯一索引值,但 阅读全文
posted @ 2024-01-10 10:38 ayanmw 阅读(202) 评论(0) 推荐(1) 编辑
摘要: 为了方便调试活动, 所以我需要修改本机时间,慢慢的往后调整, 一次调整6个小时,4次就是一天, 本来想自己写,不过,date参数总是记不住,如何获取当前时间并修改格式,传入date -s "XXXX" ,挺烦的,必应搜索没答案. 为什么不用用chatgpt呢? 我用的是ChatMoss,Vue作者主 阅读全文
posted @ 2023-06-29 10:59 ayanmw 阅读(187) 评论(0) 推荐(0) 编辑
摘要: # 起因 现在工作电脑是win10,还不太适合升级win11,但是想琢磨一下win11的 WSA (windows Subssytem For Android). 虽然国内不直接支持 安卓子系统,但是网上有办法离线安装. # 为什么要用hyper-V下安装win11+wsa ? 今天看了下vmwar 阅读全文
posted @ 2023-06-08 21:44 ayanmw 阅读(1448) 评论(0) 推荐(0) 编辑
摘要: 前言 最近要做一些前端开发, 但是我也就是刚毕业的时候用的Html5,还算熟悉,对jQuery这种语法也可以接受. 现在的都是数据驱动的前端开发, Vue3 就是新生代的代表. 还是用了TypeScript ,但是我真的对现在的web开发想吐槽 数据弱类型 习惯了golang,c++这种强类型语言, 阅读全文
posted @ 2023-04-19 11:15 ayanmw 阅读(42) 评论(0) 推荐(0) 编辑
摘要: #疑问 如何使用ffmpeg来查看类似的数据? #工具 一、ffmpeg ffmpeg属于GPL或者LGPL,确切属于哪一种,要根据编译选项,因为它里面的库有些属于GPL的有些属于LGPL的,你编译的时候打开或者关闭这些库的选项,就决定了它属于哪一种。 http://www.ffmpeg.org/l 阅读全文
posted @ 2023-04-04 20:11 ayanmw 阅读(64) 评论(0) 推荐(0) 编辑
摘要: #起因 由于我习惯使用 mysql 可视化工具, navicat, 但是有些mysql数据库是只有局域网地址的,比如某云 , 某云. 刚好navicat 有一个功能,就是 SSH tunnel 先SSH登录到目标服务器, 再从目标服务器登录mysql. 但是之前遇导过 MySQL连接错误(2013, 阅读全文
posted @ 2023-03-10 20:47 ayanmw 阅读(445) 评论(0) 推荐(0) 编辑

页脚Html代码

点击右上角即可分享
微信分享提示