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

Http Golang*bytes.Buffer nil导致致命错误,http,pointers,go,interface,Http,Pointers,Go,Interface,我遇到了与相同的问题,因为我有一个用于http请求的wrap函数 有时我需要请求: 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

我遇到了与相同的问题,因为我有一个用于http请求的wrap函数

有时我需要请求:

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)
    }
}
body
in是可选的,因此在执行
GET
请求时传递
nil
是可以接受的

问题在于
http.NewRequest
body
参数是一种接口类型:
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
*字节来调用它。缓冲区也可以:

req("GET", "http://example.com", nil) // OK

req("POST", "http://example.com", bytes.NewBufferString("data")) // Also OK

有关更多详细信息,请参见

“但我不确定这样做是否正确。”如果您的程序出现故障,则答案是“这样做不正确”。嘿,它起作用了!:)我担心这看起来是否愚蠢。正在学习:不要忽略http.NewRequest返回的错误。checkErr做什么?如果它没有终止程序,你必须在调用它后返回。您没有关闭501+响应的主体(您的意思是
=
?)您的代码有一个微妙的问题,如果第一个
resp
的StatusCode>500,您将发出另一个请求并使用结果覆盖原始
resp
值,而从未对原始
resp
调用
Body.Close()
。如果有足够的时间和足够的500多个响应,这将导致您的程序耗尽文件描述符并最终崩溃。您需要关闭两个响应的主体。还有一件事:您不能重复使用req值进行重试,因为它的主体已经被读取。您必须创建一个新请求。感谢您的帮助。我已经更新了“func req”。你认为这是对的吗?@raimondsinde调用
log.Fatal()
几乎从来都不对。您可以在一个快速演示中这样做,但在生产代码中,您永远不会希望这样做。相反,返回错误并让调用者处理它。