解组 JSON 返回错误:查找值开头的无效字符 '\x1f'

Roc*_*645 3 json go

我正在尝试编写一个命令行实用程序来发送 HTTP 请求,并在终端中打印响应正文。我正在访问的端点应该返回一个 JSON 正文

{"arr":[{"name":"Google","url":"https://www.google.com/"},{"name":"Instagram","url":"https://www.instagram.com/"},{"name":"Pinterest","url":"https://www.pinterest.com/"},{"name":"YouTube","url":"https://www.youtube.com/"}]}
Run Code Online (Sandbox Code Playgroud)

这是我的代码,body是字节数组,它是响应正文

fmt.Println(body)

var dat map[string]interface{}
if err := json.Unmarshal(body, &dat); err != nil {
    panic(err)
}
fmt.Println(dat)
Run Code Online (Sandbox Code Playgroud)

Println(body)函数返回

[31 139 8 0 0 0 0 0 0 3 170 86 74 44 42 82 178 138 174 86 202 75 204 77 85 178 82 114 207 207 79 207 73 85 210 81 42 45 202 81 178 82 202 40 41 
41 40 182 210 215 47 47 47 215 75 7 75 233 37 231 231 234 43 213 234 192 117 120 230 21 151 36 166 23 37 230 98 213 148 9 147 197 208 23 144 153 87 146 90 148 90 92 130 85 95 1 76 22 67 95 100 126 105 72 105 18 118 39 86 230 151 150 148 38 193 220 24 91 11 0 0 0 255 255 3 0 64 164 107 195 223 0 0 0]
Run Code Online (Sandbox Code Playgroud)

然后我得到这个错误

panic: invalid character '\x1f' looking for beginning of value
Run Code Online (Sandbox Code Playgroud)

我是否误解了 json.Unmarshall 函数?如何解码该字节数组并将生成的 json 对象打印到终端?

这是完整的代码

package main 

import (
    "flag"
    "fmt"
    "os"
    "net"
    "bufio"
    "strings"
    "strconv"
    "net/url"
    "encoding/json"
    "io"
)

func main() {
    
    urlPtr := flag.String("url", "", "URL to fetch. (required)")
    flag.Parse()

    if (*urlPtr == "") {
        flag.PrintDefaults()
        os.Exit(1)
    }

    u, err := url.Parse(*urlPtr)

    if err != nil {
        fmt.Println("Error parsing url")
        os.Exit(1)
    }

    path := u.Path
    hostname := u.Hostname()
    
    conn, err := net.Dial("tcp", hostname + ":80")
    if err != nil {
        fmt.Println("Error setting up tcp connection")
        os.Exit(1)
    }

    if path == "" {
        path = "/"
    }

    request := "GET " + path + " HTTP/1.1\r\n" + 
                "Host: " + hostname + ":80\r\n" + 
                "User-Agent: Go-Runtime\r\n" + 
                "Accept: */*\r\n" +
                "Accept-Encoding: gzip, deflate, br\r\n" + 
                "Connection: keep-alive\r\n\r\n"

    _, err = fmt.Fprintf(conn, request)
    if err != nil {
        fmt.Println("Error sending request to serrver")
        os.Exit(1)
    }

    r := bufio.NewReader(conn)
    var content_length int
    content_bool := false
    transfer_encoding := false 
    for {
        line, err := r.ReadString('\n')
        
        if err != nil {
            fmt.Println("Error reading header line")
            os.Exit(1)
        }

        header := strings.Split(line, ": ")      
        if header[0] == "Content-Length" {
            content_length, err = strconv.Atoi(header[1][:len(header[1]) - 2])
            if err != nil {
                fmt.Println("Error reading content length")
                os.Exit(1)
            }
            content_bool = true
        }

        if (header[0] == "Transfer-Encoding") {
            transfer_encoding = true
        }
        
        if line == "\r\n" {
            break
        }
    }

    var body []byte
    var n int

    if content_bool == true {
        body = make([]byte, content_length)
        n, err = r.Read(body)
        if err != nil {
            fmt.Println("Error reading body")
            os.Exit(1)
        }
        fmt.Println(string(body[:n]))
    }

    if transfer_encoding == true {
        
        for {
            line, err := r.ReadString('\n')
            if err != nil {
                fmt.Println("Error reading length of chunk")
                os.Exit(1)
            }
            num := line[:len(line) - 2]
            if part_length, err := strconv.ParseInt(num, 16, 64); err == nil {

                if part_length <= 0 {
                    break
                }
                body_part := make([]byte, part_length)
                if _, err := io.ReadFull(r, body_part); err != nil {
                    fmt.Println("Error reading chunk data")
                    os.Exit(1)
                }
                body = append(body, body_part...)
                _, err = r.Discard(2)
                if err != nil {
                    fmt.Println("Failed to discard trailing CRLF of chunk")
                    os.Exit(1)
                }
            } else {
                break
            }
        }

        fmt.Println(body)

        var dat map[string]interface{}
        if err := json.Unmarshal(body, &dat); err != nil {
            panic(err)
        }
        fmt.Println(dat)
        
    }

}
Run Code Online (Sandbox Code Playgroud)

小智 5

出现此错误的原因是您添加了请求标头“Accept-Encoding: gzip, deflate, br\r\n”。

所以gzip是响应编码的第一优先级。

您可以通过读取标头中的内容编码来检查响应编码。

第一个字节“\x1f”实际上是 gzip 内容的 2 个魔术字节“0x1f8b”中的 1 个。

因此,要处理 gzip,您需要先解压缩响应,然后再将其作为纯文本读取。

这是示例代码:


    // Check if the response is encoded in gzip format
    if resp.Header.Get("Content-Encoding") == "gzip" {
        reader, err := gzip.NewReader(resp.Body)
        if err != nil {
            panic(err)
        }
        defer reader.Close()
        // Read the decompressed response body
        body, err := io.ReadAll(reader)
        if err != nil {
            panic(err)
        }
        // Do something with the response body
        fmt.Println(string(body))
    } else {
        // The response is not gzip encoded, so read it directly
        body, err := io.ReadAll(resp.Body)
        if err != nil {
            panic(err)
        }
        // Do something with the response body
        fmt.Println(string(body))
    }
}
Run Code Online (Sandbox Code Playgroud)