Golang * bytes.Buffer nil导致致命错误

时间:2019-02-01 07:33:30

标签: http pointers go interface

我遇到了与https://github.com/golang/go/issues/26666相同的问题,因为我的HTTP请求具有包装功能。

有时我需要请求:

body := new(bytes.Buffer)
json.NewEncoder(body).Encode(h)
req("POST", "http://example.com", body)

有时只是:

req("GET", "http://example.com", nil)
runtime error: invalid memory address or nil pointer dereference

我最终得到:

req("GET", "http://example.com", new(bytes.Buffer))

但是我不确定这是否正确。

功能:

func req(method string, url string, body *bytes.Buffer) int {
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
if resp.StatusCode > 500 {
    time.Sleep(30 * time.Second)
    resp, err = client.Do(req)
    checkErr(err)
}
defer resp.Body.Close()
return resp.StatusCode
}

更新的功能:

func req(method string, url string, body io.Reader) int {
    req, err := http.NewRequest(method, url, body)
    req.Header.Set("Content-Type", "application/json")
    req.SetBasicAuth(user, psw)
    resp, err := client.Do(req)
    checkErr(err)
    defer resp.Body.Close()
    if resp.StatusCode >= 500 {
        time.Sleep(30 * time.Second)
        req, err := http.NewRequest(method, url, body)
        req.Header.Set("Content-Type", "application/json")
        req.SetBasicAuth(user, psw)
        resp, err := client.Do(req)
        checkErr(err)
        defer resp.Body.Close()
    }
    return resp.StatusCode
}

func checkErr(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

1 个答案:

答案 0 :(得分:4)

http.NewRequest()中的body是可选的,因此在执行nil请求时传递GET是可以接受的。

问题是body的{​​{1}}参数是接口类型:http.NewRequest,并且您试图传递具体类型为io.Reader的值。发生的情况是,该*bytes.Buffer指针将被包装在非nil接口值中,并将其作为主体传递给nil

如果您没有尸体,请像这样通过显式传递http.NewRequest

nil

然后您可以这样称呼它:

func req(method string, url string, body *bytes.Buffer) int {
    var bodyToPass io.Reader
    if body != nil {
        bodyToPass = body
    }
    req, err := http.NewRequest(method, url, bodyToPass)

    // ....
}

尽管最好的做法是,您的req("GET", "http://example.com", nil) 函数首先使用req(),所以您不必显式检查其值:

io.Reader

您也可以使用func req(method string, url string, body io.Reader) int { req, err := http.NewRequest(method, url, body) // You may pass it as-is // .... } 或非nil nil来调用它:

*bytes.Buffer

有关更多详细信息,请参见Hiding nil values, understanding why golang fails here