最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

go讀取request.Body內(nèi)容踩坑實(shí)戰(zhàn)記錄

 更新時(shí)間:2023年11月29日 09:38:39   作者:duzhenxun  
很多初學(xué)者在使用Go語(yǔ)言進(jìn)行Web開發(fā)時(shí),都會(huì)遇到讀取 request.Body內(nèi)容的問題,這篇文章主要給大家介紹了關(guān)于go讀取request.Body內(nèi)容踩坑實(shí)戰(zhàn)記錄的相關(guān)資料,需要的朋友可以參考下

前言

踩坑代碼如下,當(dāng)時(shí)是想獲取body傳過(guò)來(lái)的json

func demo(c *httpserver.Context) {
	type ReqData struct {
		Id      int        `json:"id" validate:"required" schema:"id"`
		Title   string     `json:"title"  validate:"required" schema:"title"`
		Content [][]string `json:"content" validate:"required" schema:"content"`
	}

	bodyByte, _ := io.ReadAll(c.Request.Body)
	fmt.Println(string(bodyByte))

	var req ReqData
	err := c.Bind(c.Request, &req)
	//發(fā)現(xiàn)req里的屬性還是空
	
	if err != nil {
		c.JSONAbort(nil, code.SetErrMsg(err.Error()))
		return
	}
	
	contentByte, _ := json.Marshal(req.Content)
	
	data := svc.table2DataUpdate(c.Ctx, req.Id, req.Title, req.Content) 
	c.JSON(data, err)
}
	

如上代碼Bind發(fā)現(xiàn)里面并沒有內(nèi)容,進(jìn)行追查發(fā)現(xiàn)c.Request.Body在第一次經(jīng)過(guò)io.ReadAll()調(diào)用后,再次調(diào)用時(shí)內(nèi)容已為空。

為什么會(huì)這樣??難道io.ReadAll是讀完后就給清空了嗎??

帶著這個(gè)問題對(duì)底層代碼進(jìn)行了CR,最終得到答案:不是 !!

因?yàn)閺腂ody.src.R.buf中拷貝,全拷貝完后設(shè)置b.sawEOF為true,再次讀取時(shí)遇到這個(gè)為true時(shí)就不會(huì)再讀取。

代碼CR總結(jié)

  • Body 字段是一個(gè) io.ReadCloser 類型,io.ReadCloser 類型繼承了 io.Reader 和 io.Closer 兩個(gè)接口,其中 io.Reader 接口可以通過(guò) Read 方法讀取到消息體中的內(nèi)容
  • io.ReadAll()時(shí)會(huì)先創(chuàng)建一個(gè)切片,初始化容量512,然后開始填充這個(gè)切片,中間會(huì)有一個(gè)巧妙的方式擴(kuò)容,值得學(xué)習(xí)借鑒。
  • 數(shù)據(jù)是從 b.buf(Body.src.R.buf) 中拷貝, n = copy(p, b.buf[b.r:b.w])
  • 數(shù)據(jù)循環(huán)拷貝,一直到下面幾種情況會(huì)直接返回
    • b.sawEOF==true
    • b.closed==true
    • l.N<=0(l.N指剩余內(nèi)容的數(shù)量,每讀取一段時(shí)會(huì)減掉)
  • 數(shù)據(jù)在copy過(guò)程中,會(huì)設(shè)置l.N=l.N-n 當(dāng)剩余數(shù)量為0時(shí),會(huì)設(shè)置 b.sawEOF=true

模擬一個(gè)簡(jiǎn)單的代碼

package main

import (
	"bytes"
	"errors"
	"fmt"
)

type BufDemo struct {
	buf *bytes.Buffer
	w   int
	r   int
}

var bf BufDemo

func main() {
	//初始化一個(gè)buf,模擬post提教過(guò)來(lái)的數(shù)據(jù)
	initBuf("duzhenxun")

	//可以把數(shù)據(jù)讀出
	data1 := readAll()

	//這時(shí)啥數(shù)據(jù)也沒有
	data2 := readAll()

	fmt.Println(data1, data2)
}

func readAll() []byte {
	b := make([]byte, 0, 2)
	for {
		if len(b) == cap(b) {
		  //擴(kuò)容操作
			b = append(b, 0)[:len(b)]
		}
		n, err := read(b[len(b):cap(b)])
		if err != nil && err.Error() == "EOF" {
			return b
		}
		//這行代碼能理解嗎??
		b = b[:len(b)+n]
		
    //	b[:len(b)+n] 表示對(duì)切片 b 進(jìn)行取子集操作,并返回一個(gè)新的切片。這個(gè)新的切片中包含從切片的起始元素開始,到第2個(gè)元素(不包括第2個(gè)元素)的所有元素。
    //在 Go 語(yǔ)言中,切片本身是一個(gè)包含指向底層數(shù)組的指針、長(zhǎng)度和容量等信息的結(jié)構(gòu)體,因此對(duì)切片進(jìn)行取子集操作不會(huì)創(chuàng)建新的底層數(shù)組,而只是創(chuàng)建了一個(gè)新的切片結(jié)構(gòu)體,并更新了其長(zhǎng)度和指針等信息。
    //因此,可以理解為 b[:len(b)+n]是一個(gè)新的切片,并且與原切片 b 共享同一個(gè)底層數(shù)組(指針指向相同的底層數(shù)組),但長(zhǎng)度和容量等信息可能不同。
		
	}
}

func read(p []byte) (n int, err error) {
	if bf.r == bf.w {
		return 0, errors.New("EOF")
	}
	n = copy(p, bf.buf.Bytes()[bf.r:bf.w])
	bf.r += n
	return n, nil
}

func initBuf(str string) {
	bf = BufDemo{
		buf: bytes.NewBuffer([]byte(str)),
		r:   0,
		w:   len(str),
	}
}

下面為CR的相關(guān)代碼

//src/io/io.go:626
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)]
		}
		//這里是重點(diǎn),返回copy的數(shù)量,err信息
		n, err := r.Read(b[len(b):cap(b)])
		//都讀完后會(huì)設(shè)置 body.closed=true,當(dāng)再調(diào)用r.Read時(shí)遇到b.closed=true不會(huì)再copy數(shù)據(jù),會(huì)直接返回n=0,err="http: invalid Read on closed Body"
		
		b = b[:len(b)+n]
		if err != nil {
			if err == EOF {
				err = nil
			}
			return b, err
		}
	}
}

//r.Read(b[len(b):cap(b)])
//src/net/http/transfer.go:829
func (b *body) Read(p []byte) (n int, err error) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if b.closed {
		return 0, ErrBodyReadAfterClose
	}
	return b.readLocked(p)
}

//b.readLocked(p)
//src/net/http/transfer.go:839
// Must hold b.mu.
func (b *body) readLocked(p []byte) (n int, err error) {
	if b.sawEOF {
		return 0, io.EOF
	}
	//重點(diǎn)關(guān)注
	n, err = b.src.Read(p)

	if err == io.EOF {
		b.sawEOF = true
		// Chunked case. Read the trailer.
		if b.hdr != nil {
			if e := b.readTrailer(); e != nil {
				err = e
				// Something went wrong in the trailer, we must not allow any
				// further reads of any kind to succeed from body, nor any
				// subsequent requests on the server connection. See
				// golang.org/issue/12027
				b.sawEOF = false
				b.closed = true
			}
			b.hdr = nil
		} else {
			// If the server declared the Content-Length, our body is a LimitedReader
			// and we need to check whether this EOF arrived early.
			if lr, ok := b.src.(*io.LimitedReader); ok && lr.N > 0 {
				err = io.ErrUnexpectedEOF
			}
		}
	}

	// If we can return an EOF here along with the read data, do
	// so. This is optional per the io.Reader contract, but doing
	// so helps the HTTP transport code recycle its connection
	// earlier (since it will see this EOF itself), even if the
	// client doesn't do future reads or Close.
	if err == nil && n > 0 {
		if lr, ok := b.src.(*io.LimitedReader); ok && lr.N == 0 {
			err = io.EOF
			b.sawEOF = true
		}
	}

	if b.sawEOF && b.onHitEOF != nil {
		b.onHitEOF()
	}

	return n, err
}


//b.src.Read 
//src/io/io.go:466
func (l *LimitedReader) Read(p []byte) (n int, err error) {
	if l.N <= 0 {
		return 0, EOF
	}
	if int64(len(p)) > l.N {
		p = p[0:l.N]
	}
	n, err = l.R.Read(p)
	l.N -= int64(n)
	return
}

//l.R.Read(p)
//src/bufio/buffio.go:198

// Read reads data into p.
// It returns the number of bytes read into p.
// The bytes are taken from at most one Read on the underlying Reader,
// hence n may be less than len(p).
// To read exactly len(p) bytes, use io.ReadFull(b, p).
// At EOF, the count will be zero and err will be io.EOF.
func (b *Reader) Read(p []byte) (n int, err error) {
	n = len(p)
	if n == 0 {
		if b.Buffered() > 0 {
			return 0, nil
		}
		return 0, b.readErr()
	}
	if b.r == b.w {
		if b.err != nil {
			return 0, b.readErr()
		}
		if len(p) >= len(b.buf) {
			// Large read, empty buffer.
			// Read directly into p to avoid copy.
			n, b.err = b.rd.Read(p)
			if n < 0 {
				panic(errNegativeRead)
			}
			if n > 0 {
				b.lastByte = int(p[n-1])
				b.lastRuneSize = -1
			}
			return n, b.readErr()
		}
		// One read.
		// Do not use b.fill, which will loop.
		b.r = 0
		b.w = 0
		n, b.err = b.rd.Read(b.buf)
		if n < 0 {
			panic(errNegativeRead)
		}
		if n == 0 {
			return 0, b.readErr()
		}
		b.w += n
	}

	// copy as much as we can
	n = copy(p, b.buf[b.r:b.w])
	b.r += n
	b.lastByte = int(b.buf[b.r-1])
	b.lastRuneSize = -1
	return n, nil
}

總結(jié) 

到此這篇關(guān)于go讀取request.Body內(nèi)容踩坑的文章就介紹到這了,更多相關(guān)go讀request.Body內(nèi)容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用Go語(yǔ)言編寫一個(gè)毫秒級(jí)生成組件庫(kù)文檔工具

    使用Go語(yǔ)言編寫一個(gè)毫秒級(jí)生成組件庫(kù)文檔工具

    在開發(fā)組件庫(kù)的過(guò)程中,文檔無(wú)疑是不可或缺的一環(huán),在本文中將嘗試將Go語(yǔ)言與前端技術(shù)巧妙融合,以創(chuàng)建一款能在毫秒級(jí)別完成文檔生成的工具,需要的可以參考下
    2024-03-03
  • go數(shù)據(jù)結(jié)構(gòu)和算法BitMap原理及實(shí)現(xiàn)示例

    go數(shù)據(jù)結(jié)構(gòu)和算法BitMap原理及實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了go數(shù)據(jù)結(jié)構(gòu)和算法BitMap原理及實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 利用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單Ping過(guò)程的方法

    利用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單Ping過(guò)程的方法

    相信利用各種語(yǔ)言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過(guò)程,而對(duì)Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個(gè)ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計(jì)。
    2016-09-09
  • 淺談Go1.18中的泛型編程

    淺談Go1.18中的泛型編程

    本文主要介紹了Go1.18中的泛型編程,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Go語(yǔ)言如何獲取goroutine的id

    Go語(yǔ)言如何獲取goroutine的id

    在Go語(yǔ)言中,獲取?goroutine的id并不像其他編程語(yǔ)言那樣容易,但依然有辦法,這篇文章就來(lái)和大家聊聊具體實(shí)現(xiàn)的方法,感興趣的小伙伴可以了解下
    2024-12-12
  • Golang如何將日志以Json格式輸出到Kafka

    Golang如何將日志以Json格式輸出到Kafka

    這篇文章主要介紹了Golang將日志以Json格式輸出到Kafka的方法,這篇文章還會(huì)提供一種輸出Json格式日志的方法,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • Go語(yǔ)言底層原理互斥鎖的實(shí)現(xiàn)原理

    Go語(yǔ)言底層原理互斥鎖的實(shí)現(xiàn)原理

    這篇文章主要介紹了Go語(yǔ)言底層原理互斥鎖的實(shí)現(xiàn)原理,Go?sync包提供了兩種鎖類型,分別是互斥鎖sync.Mutex和讀寫互斥鎖sync.RWMutex,都屬于悲觀鎖,更多相關(guān)內(nèi)容需要的朋友可以查看下面文章內(nèi)容
    2022-08-08
  • Go?easyjson使用及反射原理

    Go?easyjson使用及反射原理

    這篇文章主要介紹了Go?easyjson使用技巧,詳細(xì)介紹了go自帶JSON庫(kù)使用的反射原理,性能相對(duì)較差,可以使用easyjson代替,需要的朋友可以參考下
    2022-04-04
  • golang 流式讀取和發(fā)送使用場(chǎng)景示例

    golang 流式讀取和發(fā)送使用場(chǎng)景示例

    這篇文章主要為大家介紹了golang 流式讀取和發(fā)送使用場(chǎng)景示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go語(yǔ)言string,int,int64 ,float之間類型轉(zhuǎn)換方法

    Go語(yǔ)言string,int,int64 ,float之間類型轉(zhuǎn)換方法

    Go語(yǔ)言中int類型和string類型都是屬于基本數(shù)據(jù)類型,兩種類型的轉(zhuǎn)化都非常簡(jiǎn)單。下面通過(guò)本文給大家分享Go語(yǔ)言string,int,int64 ,float之間類型轉(zhuǎn)換方法,感興趣的朋友一起看看吧
    2017-07-07

最新評(píng)論

称多县| 尚志市| 安阳市| 勃利县| 沁水县| 庆阳市| 娱乐| 崇信县| 湟源县| 邵阳市| 册亨县| 庆阳市| 青海省| 双峰县| 遵义市| 沅江市| 贵阳市| 株洲市| 苏尼特左旗| 双柏县| 黄龙县| 汕头市| 湖北省| 乐清市| 龙泉市| 正镶白旗| 马关县| 南开区| 信阳市| 凤冈县| 保德县| 馆陶县| 婺源县| 财经| 四子王旗| 广东省| 大化| 昌乐县| 雅江县| 奉节县| 正定县|