抄github上的golang代码被坑后,弄懂了gin的原理
搬运代码, 高高兴兴gin 是 Golang 中很火的 Web 框架. 最近我有一个拦截 gin response 返回值并记录日志的需求.
显然使用 gin 的 middleware 来实现最合适. 我搜索后发现 github 上 gin issue 中有人给出了相关实现,还有好几个赞, 于是乎我就高高兴兴的把代码抄下来了.
当时我也测了一下, 发现没毛病, 便稍加改动上线了 ...
不好, 有 bug 了!结果上线不到一天, 我就发现问题了, 咦, 怎么有的 response 返回值没有输出呢?
经过一番探索, 这个 bug 在我本地复现了. 上面代码中函数 sayHello 调用 c.JSON 来响应, 如果改为调用 c.String 也就是response 返回值为一个字符串, 那么 logResponseBody 这个 middleware 函数就 hook 不到 response 返回值了.
// sayHello 这样改动后, // logResponseBody 这个 middleware 函数// 就 hook 不到 response 返回值了func sayHello(c *gin.Context) { //c.JSON(200, gin.H{ // "hello": "privationel", //}) c.String(200, "hello world")}
解析 github 上的代码我搬运的代码:
文章插图
代码中 responseBodyWriter 结构体实现的是 gin.ResponseWriter 接口.
注意一下其中的 Write 方法, 这个方法把 response 返回值缓存到 responseBodyWriter 结构体的 body 属性中, 后面会用来输出 response 返回值.
我对相关代码, 加了注释:
func (r responseBodyWriter) Write(b []byte) (int, error) {// b 就是 response// Write 方法把 response 缓存到 responseBodyWriter 结构体的 body 属性中 r.body.Write(b) return r.ResponseWriter.Write(b)}
最终 logResponseBody 函数负责打印 response 返回值, 结合相关注释理解下:
func logResponseBody(c *gin.Context) { w :=而 c.String 调用的是 gin.ResponseWriter.WriteString 输出返回值. 函数调用关系图如下:
文章插图
我把相关的源码贴上, 再来验证一下.
c.JSON 的 Render 方法中调用了 WriteJSON, 在 WriteJSON 中调用的是 ResponseWriter.Write 方法. 源码如下图所示.
文章插图
c.String 的 Render 方法中调用了 WriteString, 在 WriteString 中调用的是 io.WriteString.
文章插图
io.WriteString 的实现, 如下图所示, 调用的是 sw.WriteString, 也就是 gin.ResponseWriter 接口中的 WriteString 方法, 而高票答案中没有实现这个方法.
文章插图
修复bug理解了以上内容, 修复 bug 也非常简单, 只需要重写一下 WriteString 方法就可以了:
func (r responseBodyWriter) WriteString(s string) (n int, err error){ r.body.WriteString(s) return r.ResponseWriter.WriteString(s)}
补充【抄github上的golang代码被坑后,弄懂了gin的原理】查看扩展链接, 可以看到 github 上的那个 issue.
推荐阅读
- 雷军发布会上的话!让人看到了雷军的“另一面”!网友:有良心
- 管管风口上的“速成培训”
- 程序员开发抢茅台脚本:2天就刷榜Github
- 英特尔Xe GPU在Linux 5.11上的性能表现不错
- 手掌上的百英寸大屏,这神器如何炼成 理光新款LED短焦投影给出答案
- 外媒:苹果正在修复M1 Mac上的超宽显示分辨率问题
- 不迷路也不掉坑 盘点扫地机器人身上的那些传感器
- 心灵上的垃圾也需要清理,这六条能不能帮到你,让你不再浮躁
- 科罗拉多州立大学设计出能让无人机悬挂在物体上的机械夹具
- 苹果手机上的“耳朵”图标,原来是个监听器!很多人都不知道