如何判断流式HTTP响应中byte数组rb的完成时机以进行JSON解码?
问题分析与解决方案
你忽略的核心关键点是:当ReadBytes返回io.EOF时,就代表整个分块HTTP响应已经读取完毕,此时rb就是完整的响应数据,可直接进行JSON解码。
Go的http包会自动处理分块传输编码的解码,你从resp.Body读到的就是原始的响应内容,不需要手动处理分块的边界标识,io.EOF就是整个响应结束的标志。
以下分两种常见场景给出修改后的代码:
场景1:服务端返回单个完整JSON(分块传输)
如果服务端返回的是一个完整的JSON结构,只是通过分块传输,那么在循环结束后即可解码rb:
package main import ( "bufio" "bytes" "encoding/json" "fmt" "io" "net/http" ) // 根据服务端实际返回的JSON结构定义结构体 type ResponseData struct { Message string `json:"message"` Data any `json:"data"` } func main() { body := []byte("test") resp, err := http.Post("http://localhost:8281/tap", "application/json", bytes.NewReader(body)) if err != nil { fmt.Printf("%v\n", err) return } defer resp.Body.Close() fmt.Printf("Status: [%s]\n", resp.Status) fmt.Println() reader := bufio.NewReader(resp.Body) var rb []byte for { line, err := reader.ReadBytes('\n') if err != nil { if err == io.EOF { // 读取到最后一块数据,追加剩余内容后退出循环 rb = append(rb, line...) break } fmt.Printf("Error reading streamed bytes: %v\n", err) return } rb = append(rb, line...) } // 循环结束,rb已包含完整响应,开始JSON解码 var result ResponseData if err := json.Unmarshal(rb, &result); err != nil { fmt.Printf("JSON decode error: %v\n", err) return } fmt.Printf("Decoded result: %+v\n", result) }
场景2:服务端返回JSON流(每行一个独立JSON对象)
如果服务端是每行返回一个独立的JSON对象(JSON Lines格式),则无需等待全部读取,每行读取后即可解码:
package main import ( "bufio" "bytes" "encoding/json" "fmt" "net/http" ) // 每行JSON对应的结构体 type LineData struct { ID int `json:"id"` Text string `json:"text"` } func main() { body := []byte("test") resp, err := http.Post("http://localhost:8281/tap", "application/json", bytes.NewReader(body)) if err != nil { fmt.Printf("%v\n", err) return } defer resp.Body.Close() fmt.Printf("Status: [%s]\n", resp.Status) fmt.Println() scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { line := scanner.Bytes() var data LineData if err := json.Unmarshal(line, &data); err != nil { fmt.Printf("Decode line error: %v\n", err) continue } fmt.Printf("Decoded line: %+v\n", data) } if err := scanner.Err(); err != nil { fmt.Printf("Scanner error: %v\n", err) } }
额外简化方案
如果响应数据量不大,可直接用io.ReadAll一次性读取整个响应体,无需循环:
rb, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("Read error: %v\n", err) return } // 后续解码逻辑同上
内容的提问来源于stack exchange,提问作者user1513388
相关产品推荐
相关产品推荐

