golang多次读取http request body问题分析

这篇具有很好参考价值的文章主要介绍了golang多次读取http request body问题分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

问题起因

使用postman发送了一个http请求,对每个请求都有一个对应的context:

type APIContext struct {
	Action   string
	ID       string
	Type     string
	Link     string
	Method   string
	Version  *APIVersion
	Request  *http.Request
	Response http.ResponseWriter
	...
}

其中Request成员变量是golang1.17.3版本http库中定义的Request结构(这里贴出部分成员变量):

type Request struct {
    Method string
    URL *url.URL
    Header Header
    Body io.ReadCloser
    GetBody func() (io.ReadCloser, error)
    Response *Response
    ctx context.Context
    ...
}

请求处理的代码使用ReadAll方法读取Request.Body,debug发现读取出来的字节切片为空:

func ApiHandler() (error) {
    ...
    bodyBytes, err := ioutil.ReadAll(request.Request.Body)
    // fmt.Printf("bodyBytes: %+v", bodyBytes) 结果为[]
    if err != nil {
        ...
    }
    ...
}

问题探究

我把这个问题发给了gpt
go语言如果想多次使用 request body,Go语言,golang,http,开发语言
gpt回答说可能是由于Body已经被读取过一次,事实上,我的代码之前确实使用过ReadBody方法读取了一次:

func ApiHandler2() (error) {
    input, err := parse.ReadBody(request.Request)
    ...
}

这个parse.ReadBody是公司的库代码,在此不深入分析

出于好奇,我问了gpt官方库中ioutil.ReadAll()方法能否多次读取Request.Body
go语言如果想多次使用 request body,Go语言,golang,http,开发语言
gpt回答ReadAll()方法读取了一次就会消耗掉Request.Body,不能再次读取,并提供两种方法再次读取:

  1. 将读取的Request.Body缓存到一个变量bodyBytes(字节切片类型),后续需要读取使用该变量
  2. 使用ioutol.NopCloser方法写回到Request.Body

问题溯源

来研究一下ioutil.ReadAll()源码:

// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EOF from Read
// as an error to be reported.
func ReadAll(r Reader) ([]byte, error) {
	b := make([]byte, 0, 512)
	for {
		if len(b) == cap(b) {
			// Add more capacity (let append pick how much).
			b = append(b, 0)[:len(b)]
		}
		n, err := r.Read(b[len(b):cap(b)])
		b = b[:len(b)+n]
		if err != nil {
			if err == EOF {
				err = nil
			}
			return b, err
		}
	}
}

函数的作用是初始化一个字节切片缓冲区,不断调用Read方法读取数据,直到EOF为止

缓冲区b的初始大小只有512个字节,如果缓冲区满(len(b)==cap(b)),则向b添加一个0元素触发切片的扩容机制,并去掉添加的"0"元素([:len(b)]),之后一直读取数据,可能缓冲区又会满,会继续扩容的操作,直到读取到EOF

从对ReadAll()方法的分析可以得知,使用ReadAll函数处理数据时,内存消耗随着数据的增大而增加,处理较大数据时,会触发多次扩容机制,需要分配大量内存。加载一个10M的文件,可能就需要50M的内存分配

io.Reader 接口中的 Read 方法的定义如下:

type Reader interface {
    Read(p []byte) (n int, err error)
}

这个方法接收一个字节数组 p 作为参数,返回两个值,一个是 n 表示读取的字节数,另一个是 err 表示可能出现的错误。不同的数据源类型实现方式不同

Request.Body只能读取一次的原因是因为,在第一次对其进行读取时,指针已经移动到了 EOF(End Of File)位置,再次读取时就无法再次从头开始读取了

题外话

关于ReadAll()方法消耗内存的替代方案有两种:

  1. 使用io.ReadFile函数
  2. 使用io.Copy函数
  • ReadFile函数定义如下:
func ReadFile(filename string) ([]byte, error){}

传参为待加载文件的路径,返回为文件的内容文章来源地址https://www.toymoban.com/news/detail-837994.html

  • io.Copy会拷贝数据,少于ReadAll的内存消耗

到了这里,关于golang多次读取http request body问题分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • post请求出现required request body is missing错误的问题所在?

    后端接口查询获取数据库中的数据,前端接受数据进行列表展示。 后端接口swagger测试无误,前端报错500:required request body is missing 给出以下两点原因及其方案: 1.后端原因:controller中该接口函数的参数应为请求体@RequestBody,而不是@RequestParam 改为: 2.前端原因:POST与GET请求

    2024年02月04日
    浏览(75)
  • Go语言网络编程:HTTP服务端之底层原理与源码分析——http.HandleFunc()、http.ListenAndServe()

    在 Golang只需要几行代码便能启动一个 http 服务,在上述代码中,完成了两件事: 调用 http.HandleFunc 方法,注册了对应于请求路径 /ping 的 handler 函数 调用 http.ListenAndServe,启动了一个端口为 8999 的 http 服务 2.1 server 结构 Addr :表示服务器监听的地址。如\\\":8080\\\"表示服务器在本地

    2024年02月08日
    浏览(62)
  • HTTP的请求方法,空行,body,介绍请求报头的内部以及粘包问题

    目录 一、GET与POST简介 二、空行和body 三、初识请求报头以及粘包问题 四、认识请求报头剩余部分 GET https://www.sogou.com/HTTP/1.1 请求报文中的方法,是最常规的方法(获取资源) POST:传输实体主体的方法 一般来说方法的比重 GET占据八成 POST占据一成 其他的各种杂七杂八的方法

    2024年02月08日
    浏览(34)
  • Go For Web:Golang http 包详解(源码剖析)

    本文作为解决如何通过 Golang 来编写 Web 应用这个问题的前瞻,对 Golang 中的 Web 基础部分进行一个简单的介绍。目前 Go 拥有成熟的 Http 处理包,所以我们去编写一个做任何事情的动态 Web 程序应该是很轻松的,接下来我们就去学习了解一些关于 Web 的相关基础,了解一些概念,

    2023年04月14日
    浏览(35)
  • docker 拉取镜像出现 error parsing HTTP 408 response body 错误问题

    在使用 docker 下拉镜像仓库时,出现http解析错误。具体错误log信息如下: error parsing HTTP 408 response body: invalid character ‘’ looking for beginning of value: “ nYour browser didn’t send a complete request in time.nn” 在尝试搜了很多答案后,都没有解决问题,包括如下的解决方案: https://www.

    2024年02月12日
    浏览(74)
  • Golang:Go语言结构

    在我们开始学习 Go 编程语言的基础构建模块前,让我们先来了解 Go 语言最简单程序的结构。 Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 表达式 注释 接下来让我们来看下简单的代码,该代码输出了\\\"Hello World!\\\": 让我们来看下以上程序的各个部分: 第一

    2024年02月10日
    浏览(59)
  • Go语言入门记录:从channel的池应用、sync的Pool、benchmark、反射reflect、json处理、http、性能分析和一些编程习惯

    channel的一对一会阻塞,添加buffer不会阻塞。 buffered Channel实现对象池。 sync.Pool 的介绍。 获取时先去私有对象中获取,如果不存在就在相同Processor中的共享池中获取,如果还没有,则去其他Processor中去获取。 存放时,如果私有对象不存在,就放在私有对象中,如果存在就放在

    2024年02月10日
    浏览(50)
  • python 爬虫热身篇 使用 requests 库通过 HTTP 读取网络数据,使用 pandas 读取网页上的表格,使用 Selenium 模拟浏览器操作

    在过去,收集数据是一项繁琐的工作,有时非常昂贵。机器学习项目不能没有数据。幸运的是,我们现在在网络上有很多数据可供我们使用。我们可以从 Web 复制数据来创建数据集。我们可以手动下载文件并将其保存到磁盘。但是,我们可以通过自动化数据收集来更有效地做

    2023年04月08日
    浏览(74)
  • 【GoLang】MAC安装Go语言环境

    小试牛刀 首先安装VScode软件 或者pycharm mac安装brew软件  brew install go 报了一个错误 不提供这个支持  重新brew install go 之后又重新brew reinstall go 使用go version 可以看到go 的版本 使用go env  可以看到go安装后的配置 配置一个环境变量 vim ~/.zshrc,  

    2024年02月15日
    浏览(60)
  • Go语言(Golang)数据库编程

    要想连接到 SQL 数据库,首先需要加载目标数据库的驱动,驱动里面包含着于该数据库交互的逻辑。 sql.Open() 数据库驱动的名称 数据源名称 得到一个指向 sql.DB 这个 struct 的指针 sql.DB 是用来操作数据库的,它代表了0个或者多个底层连接的池,这些连接由sql 包来维护,sql 包会

    2024年02月03日
    浏览(93)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包