转自知乎:如何在golang http服务端程序中读取2次Request Body? - 知乎

提问:

在golang http服务端程序中,我想在真正处理Request Body之前将Body中的内容记录到日志中.

实际上这一需求就是要在Request Body中读取2次数据,由于Body为`ReadCloser` 类型,读取一次之后就无法再次进行读取,就需要读取完之后对Body重新赋值来支持后续的读取操作, 网上看了大家一般都是这样实现的.

bodyBytes, _ := ioutil.ReadAll(req.Body)
req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

但是这样做实际上覆盖了原来的Body,原来的Body是实现了Close方法的,这里直接对Body赋值,是否需要对原来的Body调用 Close方法?

bodyBytes, _ := ioutil.ReadAll(req.Body)
req.Body.Close()  //  这里调用Close
req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

官方的代码注释里虽然写了不需要在处理函数里调用Close:Request.Body:"The Server will close the request body. The ServeHTTP Handler does not need to."

但是在处理完成之后http框架会去调用Close做一些操作,finishRequest() ,如果在处理程序里对Body做了替换,这里调用的Close就是我替换后的 NopCloser.

How to read response body twice in Golang middleware?​stackoverflow.com/questions/46948050/how-to-read-response-body-twice-in-golang-middleware正在上传…重新上传取消

里有条评论: No, the original "close" is not lost. You merely assign a new value to the exportedRequest.Bodyfield. This is not the only reference to the original body reader that needs to be closed.– iczaOct 26 '17 at 9:40

想请问下各位熟悉golang http包的大神,这里直接对Body进行覆盖不显示调用Close 到底会不会造成不好的影响!

回答:

作者:波罗学
链接:https://www.zhihu.com/question/329045911/answer/714781838
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

的确从网上没有找到为什么?

网上找不到答案,没办法,只看自己看源码了。顺着服务的启动流程找到了原本执行 reqBody.Close 的地方。在 server.go 文件中的 finishRequest 函数:

// Close the body (regardless of w.closeAfterReply) so we can
// re-use its bufio.Reader later safely.
w.reqBody.Close()

就是这段代码,注释意思是,关闭 body 是为了之后可以安全的重用 bufio.Reader。这个 bufio.Reader 在哪里可以看到,,看下 body 的定义,如下:

type body struct {src          io.Readerhdr          interface{}   // non-nil (Response or Request) value means read trailerr            *bufio.Reader // underlying wire-format reader for the trailerclosing      bool          // is the connection to be closed after reading body?doEarlyClose bool          // whether Close should stop earlymu         sync.Mutex // guards following, and calls to Read and ClosesawEOF     boolclosed     boolearlyClose bool   // Close called and we didn't read to the end of srconHitEOF   func() // if non-nil, func to call when EOF is Read
}

body 中有个 r 字段,类型是 bufio.Reader,用于从底层读取请求数据。它是请求连接传递过来的。

现在的问题是,w.reqBody 已经不是最初的那个 body 了,已经被 middleware 重置了。所以,在 finishRequest 中关闭 body 是没有用的,现在的 Body 是 NopCloser 类型,即 Close 中是空操作。要想重用 bufio.Reader,只有在 middleware 重置repBody 的时候,关闭它。


补充:

发现了个问题,如题主评论区所言,middleware 中的 req.Body 和 response 中的 reqBody 是两个变量。初期,req.Body 和 reqBody 中存放了同一个地址。但是,当 req.body = io.NoCloser 时,只是改变了 req.Body 中的指针,而 reqBody 仍旧指向原始请求的 body,故不需要在 middleware 中执行关闭。

我突然在想,是 stackoverflow 错了,还是 net/http 的历史版本中关闭的是 req.body 呢?而后来通过引进 reqBody 修复这个不算 bug 的 bug呢?

最终找到了这个提交记录,在 Go 1.6 已经不用担心这个问题了。提交记录如下:

net/http: don't panic after request if Handler sets Request.Body to nil · golang/go@b105054​github.com/golang/go/commit/b1050542c1dcff9f5902ab2745aae3ccc8340c11#diff-bf538ad0b0c9263061db13ca6d8f324e正在上传…重新上传取消

提交信息:

net/http: don't panic after request if Handler sets Request.Body to nil。

大致的意思是,不用再担心把 req.Body 设置 nil,其实也就是不用再担心重置 req.Body 了。


关于怎么读这个源码?

了解几部分就行了,一个服务启动的 serve.go,两个函数,一个是 Serve,它启动服务,另一个是 Serve 函数的最后部分, go c.serve 启动一个协程处理请求。而 finishRequest 就在 c.serve 中。找 body 继续往下追吧!不说了。

如何在golang http服务端程序中读取2次Request Body?(转)相关推荐

  1. 也谈如何构建高性能服务端程序

    引子:我接触过很多编程语言,接触过各种各样的服务器端开发,Java,Go,Ruby,Javascript等语言,Spring,Node.js,Rails等等常见服务器端框架和编程模型都有接触.这里谈一 ...

  2. 【技术分享】linux各种一句话反弹shell总结——攻击者指定服务端,受害者主机(无公网IP)主动连接攻击者的服务端程序(CC server),开启一个shell交互,就叫反弹shell。...

    反弹shell背景: 想要搞清楚这个问题,首先要搞清楚什么是反弹,为什么要反弹. 假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常 ...

  3. 服务端程序的初步实现

    文章目录 1 服务端程序的初步实现 1.1 设计实现 1.2 代码实现 1 服务端程序的初步实现 1.1 设计实现 服务端设计初步: 设计要素分析: 一般情况下,聊天服务端只负责消息传递. 客户端的连 ...

  4. 基于半同步/半反应堆线程池实现的HTTP解析服务端程序

    简介: 半同步/半反应堆线程池是通过一个线程往工作队列添加任务T,然后工作线程竞争工作队列获得任务T.HTTP请求解析服务端程序:逐行解析客户端发送来的HTTP请求然后作出HTTP回答.采用线程池就是 ...

  5. SharedCache分析:服务端程序

    SharedCache由3个主要的项目组成MergeSystem.Indexus.WinServiceCommon.MergeSystem.Indexus.WinService和MergeSystem ...

  6. 三角定位法java代码_GitHub - megagao/IndoorPos: 这是一个采用蓝牙4.0--iBeacon技术的室内定位服务端程序。...

    IndoorPos 这是一个采用iBeacon技术的室内定位服务端程序,里面包含了三种定位算法,三边定位算法.加权三边定位算法和三角形加权质心定位算法.程序采用Spring框架,简化了JDBC和RMI ...

  7. winform服务器消息推送,winform项目——仿QQ即时通讯程序12:服务端程序补充及优化...

    原标题:winform项目--仿QQ即时通讯程序12:服务端程序补充及优化 上一篇文章大概完成了服务端程序,今天继续做项目的时候发现还有一些功能没有做,还有几处地方不够完善.不做好就会影响客户端程序的 ...

  8. 用于溢出漏洞研究的Socket服务端程序

    备注 by jhonguy: char recvBuf[5000]={0};  recv(sockConn,recvBuf,5000,0);  char sendBuf[10]={0};  sendB ...

  9. 物联网系统上位机源码,含服务器和客户端 物联网服务端程序

    物联网系统上位机源码,含服务器和客户端 物联网服务端程序,可以接受市面上大多数透传数据的DTU登录,以及和DTU双向通讯 程序功能:能分组管理,不同的组别用户只可见自己组别的设备,设备和客户端登录掉线 ...

最新文章

  1. redis在微服务领域的贡献
  2. 揭秘又拍云凭啥做到两年估值超10亿?
  3. iphone照片删掉又出现_iPhone 内存不够用,原因在这儿!
  4. 【机器学习】 ID3,C4.5,CART决策树
  5. t检验的p值对照表_论文数据分析实战 | 如何对汇总数据进行t检验
  6. 哈工大华中科技大学计算机学院官网,我国重点大学排名盘点,哈工大重回前十,北理工只排十四...
  7. 【平台兼容性】jeecg部署weblogic 测试,修改配置方法
  8. 05_Grafana的安装和Influxdb数据源配置
  9. Handler.postDelayed(new Runnable)是否运行在主线程
  10. 11.1-全栈Java笔记:多线程技术的基本概念
  11. ASP.NET上传下载文件
  12. 【细节实现题】LeetCode 8. String to Integer (atoi)
  13. c++中new和delete
  14. InstallShield 教程
  15. mac卸载mysql
  16. 基于Django框架的物联网空气质量监测系统的实现
  17. IDEA必备插件大全
  18. kubectl源码分析之rollout status
  19. 《IT项目经理成长手记》读后有所思
  20. mailgun php 邮件发送 实例

热门文章

  1. Docker基本命令入门
  2. Hadoop(HDFS,YARN)的HA集群安装
  3. DDD(领域驱动设计)
  4. oracle 列 连续,sql 查寻某列连续的几个值是否相同
  5. python urllib3 request 无返回结果_python urllib request urlopen请求网页返回bytes类型
  6. pythonclass全局变量_Python的变量(全局变量、局部变量、类变量和实例变量)
  7. qt 等待线程结束_实战PyQt5: 128-使用多线程进行并行处理
  8. java中断响应时间_Java多线程 sleep方法响应中断 sleep面试问题(与wait/notify的
  9. OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据...
  10. SICP:Building Abstractions with Data