欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang多次讀取http request body的問題分析

 更新時間:2024年01月28日 10:31:39   作者:MinutkiBegut  
這篇文章主要給大家分析了golang多次讀取http request body的問題,文中通過代碼示例和圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下

問題起因

使用postman發(fā)送了一個http請求,對每個請求都有一個對應(yīng)的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結(jié)構(gòu)(這里貼出部分成員變量):

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發(fā)現(xiàn)讀取出來的字節(jié)切片為空:

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

問題探究

我把這個問題發(fā)給了gpt

在這里插入圖片描述

gpt回答說可能是由于Body已經(jīng)被讀取過一次,事實上,我的代碼之前確實使用過ReadBody方法讀取了一次:

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

這個parse.ReadBody是公司的庫代碼,在此不深入分析

出于好奇,我問了gpt官方庫中ioutil.ReadAll()方法能否多次讀取Request.Body

在這里插入圖片描述

gpt回答ReadAll()方法讀取了一次就會消耗掉Request.Body,不能再次讀取,并提供兩種方法再次讀?。?/p>

  • 將讀取的Request.Body緩存到一個變量bodyBytes(字節(jié)切片類型),后續(xù)需要讀取
  • 使用該變量使用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
		}
	}
}

函數(shù)的作用是初始化一個字節(jié)切片緩沖區(qū),不斷調(diào)用Read方法讀取數(shù)據(jù),直到EOF為止

緩沖區(qū)b的初始大小只有512個字節(jié),如果緩沖區(qū)滿(len(b)==cap(b)),則向b添加一個0元素觸發(fā)切片的擴(kuò)容機(jī)制,并去掉添加的"0"元素([:len(b)]),之后一直讀取數(shù)據(jù),可能緩沖區(qū)又會滿,會繼續(xù)擴(kuò)容的操作,直到讀取到EOF

從對ReadAll()方法的分析可以得知,使用ReadAll函數(shù)處理數(shù)據(jù)時,內(nèi)存消耗隨著數(shù)據(jù)的增大而增加,處理較大數(shù)據(jù)時,會觸發(fā)多次擴(kuò)容機(jī)制,需要分配大量內(nèi)存。加載一個10M的文件,可能就需要50M的內(nèi)存分配

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

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

這個方法接收一個字節(jié)數(shù)組 p 作為參數(shù),返回兩個值,一個是 n 表示讀取的字節(jié)數(shù),另一個是 err 表示可能出現(xiàn)的錯誤。不同的數(shù)據(jù)源類型實現(xiàn)方式不同

Request.Body只能讀取一次的原因是因為,在第一次對其進(jìn)行讀取時,指針已經(jīng)移動到了 EOF(End Of File)位置,再次讀取時就無法再次從頭開始讀取了

題外話

關(guān)于ReadAll()方法消耗內(nèi)存的替代方案有兩種:

  • 使用io.ReadFile函數(shù)
  • 使用io.Copy函數(shù)

ReadFile函數(shù)定義如下:

func ReadFile(filename string) ([]byte, error){}

傳參為待加載文件的路徑,返回為文件的內(nèi)容

  • io.Copy會拷貝數(shù)據(jù),少于ReadAll的內(nèi)存消耗

以上就是golang多次讀取http request body的問題分析的詳細(xì)內(nèi)容,更多關(guān)于golang多次讀取http request body的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何使用騰訊云go sdk 查詢對象存儲中最新文件

    如何使用騰訊云go sdk 查詢對象存儲中最新文件

    這篇文章主要介紹了使用騰訊云go sdk 查詢對象存儲中最新文件,這包括如何創(chuàng)建COS客戶端,如何逐頁檢索對象列表,并如何對結(jié)果排序以找到最后更新的對象,我們還展示了如何優(yōu)化用戶體驗,通過實時進(jìn)度更新和檢索多個文件來改進(jìn)程序,需要的朋友可以參考下
    2024-03-03
  • Golang HTTP 服務(wù)平滑重啟及升級的思路

    Golang HTTP 服務(wù)平滑重啟及升級的思路

    Golang HTTP服務(wù)在上線時,需要重新編譯可執(zhí)行文件,關(guān)閉正在運(yùn)行的進(jìn)程,然后再啟動新的運(yùn)行進(jìn)程。這篇文章主要介紹了Golang HTTP 服務(wù)平滑重啟及升級,需要的朋友可以參考下
    2020-04-04
  • Golang讀寫二進(jìn)制文件方法總結(jié)

    Golang讀寫二進(jìn)制文件方法總結(jié)

    使用?Golang?的?encoding/gob?包讀寫二進(jìn)制文件非常方便,而且代碼量也非常少,本文就來通過兩個示例帶大家了解一下encoding/gob的具體用法吧
    2023-05-05
  • Go語言編譯程序從后臺運(yùn)行,不出現(xiàn)dos窗口的操作

    Go語言編譯程序從后臺運(yùn)行,不出現(xiàn)dos窗口的操作

    這篇文章主要介紹了Go語言編譯程序從后臺運(yùn)行,不出現(xiàn)dos窗口的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang實現(xiàn)簡易的rpc調(diào)用

    Golang實現(xiàn)簡易的rpc調(diào)用

    RPC指(Remote Procedure Call Protocol)遠(yuǎn)程過程調(diào)用協(xié)議。本文將實現(xiàn)利用Golang進(jìn)行rpc調(diào)用(只實現(xiàn)一個rpc框架基本的功能,不對性能做保證),需要的可以參考一下
    2023-03-03
  • 使用Golang編寫一個簡單的命令行工具

    使用Golang編寫一個簡單的命令行工具

    Cobra是一個強(qiáng)大的開源工具,能夠幫助我們快速構(gòu)建出優(yōu)雅且功能豐富的命令行應(yīng)用,本文將利用Cobra編寫一個簡單的命令行工具,感興趣的可以了解下
    2023-12-12
  • Windows10系統(tǒng)下安裝Go環(huán)境詳細(xì)步驟

    Windows10系統(tǒng)下安裝Go環(huán)境詳細(xì)步驟

    Go語言是谷歌推出的一款全新的編程語言,可以在不損失應(yīng)用程序性能的情況下極大的降低代碼的復(fù)雜性,這篇文章主要給大家介紹了關(guān)于Windows10系統(tǒng)下安裝Go環(huán)境的詳細(xì)步驟,需要的朋友可以參考下
    2023-11-11
  • 解析Golang中的鎖競爭問題

    解析Golang中的鎖競爭問題

    這篇文章主要介紹了golang中的鎖競爭問題,本文通過實例代碼給大家詳細(xì)講解,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-10-10
  • Golang實現(xiàn)支付寶沙箱支付的方法步驟

    Golang實現(xiàn)支付寶沙箱支付的方法步驟

    本文主要介紹了Golang實現(xiàn)支付寶沙箱支付的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 淺析Go語言如何在終端里實現(xiàn)倒計時

    淺析Go語言如何在終端里實現(xiàn)倒計時

    這篇文章主要為大家詳細(xì)介紹了Go語言中是如何在終端里實現(xiàn)倒計時的,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-03-03

最新評論