duanke8011
2019-02-01 07:33
浏览 594
已采纳

Golang * bytes.Buffer nil会导致致命错误[关闭]

I had the same issue as https://github.com/golang/go/issues/26666 because I have a wrap function for my http requests.

Sometimes I need to request:

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

And sometimes it's simply:

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

I ended up with:

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

But I'm not sure if it's the right thing to do.

The function:

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
}

Updated function:

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条回答 默认 最新

  • dqaq59269 2019-02-01 08:13
    已采纳

    The body in http.NewRequest() is optional, so passing nil is acceptable when you're doing GET requests.

    The problem is that the body parameter of http.NewRequest is an interface type: io.Reader, and you're attempting to pass a value of a concrete type *bytes.Buffer. What happens is that this nil pointer will be wrapped in a non-nil interface value, and that will be passed to http.NewRequest as the body.

    If you don't have a body, pass nil explicitly, like this:

    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)
    
        // ....
    }
    

    And then you can call it like:

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

    Although best would be if your req() function would take io.Reader in the first place, so you don't have to check its value explicitly:

    func req(method string, url string, body io.Reader) int {
        req, err := http.NewRequest(method, url, body) // You may pass it as-is
    
        // ....
    }
    

    And you can call it with nil or with a non-nil *bytes.Buffer too:

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

    For more details, see Hiding nil values, understanding why golang fails here

    打赏 评论

相关推荐 更多相似问题