http:阅读已关闭的响应正文 - httptest.NewServer

我正在尝试使用 httptest.NewServer 进行测试,但遇到了障碍。


在我的代码中,我正在向外部 API 发出 GET 请求,并且我想使用 httptest.NewServer 对此进行测试。


这是我发出请求的代码(main.go):


package main


import (

    "fmt"

    "io"

    "io/ioutil"

    "log"

    "net/http"

)


type HTTPClient interface {

    Do(req *http.Request) (*http.Response, error)

}


type NewRequest interface {

    NewRequest(method string, url string, body io.Reader) (*http.Request, error)

}


var (

    Client HTTPClient

)


func init() {

    Client = &http.Client{}

}


func main() {

    url := "https://httpbin.org/get"

    GetData(url)

}


func GetData(url string) (*http.Response, error) {

    req, err := http.NewRequest(http.MethodGet, url, nil)

    if err != nil {

        log.Fatalln(err)

        return nil, err

    }


    resp, err := Client.Do(req)


    if err != nil {

        log.Fatalln(err)

        return nil, err

    }


    defer resp.Body.Close()


    responseBody, err := ioutil.ReadAll(resp.Body)

    if err != nil {

        log.Fatal(err)

        return nil, err

    }


    fmt.Println(resp.Status)

    fmt.Println(string(responseBody))

    return resp, nil

}

当我运行它时,它工作正常。


这是我的测试文件:


package main


import (

    "fmt"

    "io/ioutil"

    "log"

    "net/http"

    "net/http/httptest"

    "testing"

)


func TestYourHTTPGet(t *testing.T){


    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        fmt.Fprintln(w, `response from the mock server goes here`)

    }))

    defer ts.Close()


    mockServerURL := ts.URL


    resp, err := GetData(mockServerURL)

    if err != nil {

        fmt.Println("Error 1: ", err)

    }

    defer resp.Body.Close()


    responseBody, err := ioutil.ReadAll(resp.Body)

    if err != nil {

        log.Fatal("Error 2: ", err)

    }


    fmt.Println(resp.Status)

    fmt.Println(string(responseBody))

}


当我运行时,go test我收到错误:http: read on closed response body。如果我defer resp.Body.Close()从main.go测试中删除正确通过。


我不确定为什么会发生这种情况,并希望有人可以解释这里发生了什么?


阿晨1998
浏览 169回答 2
2回答

慕斯王

正如@Cerise Limón 所说,你打resp.Body.Close()了两次电话,然后尝试阅读封闭的身体。要修复您的代码,您可以从GetData函数中删除主体处理并在外部执行GetData或返回主体而不在测试中读取它。main.go:package mainimport (    "fmt"    "io/ioutil"    "log"    "net/http")var Client = &http.Client{}func main() {    url := "https://httpbin.org/get"    status, data, err := GetData(url)    if err != nil {        log.Fatalln(err)    }    fmt.Println(status)    fmt.Println(string(data))}func GetData(url string) (status string, body []byte, err error) {    req, err := http.NewRequest(http.MethodGet, url, nil)    if err != nil {        return    }    resp, err := Client.Do(req)    if err != nil {        return    }    defer resp.Body.Close()    body, err = ioutil.ReadAll(resp.Body)    if err != nil {        log.Fatalln(err)    }    return resp.Status, body, nil}main_test.go:package mainimport (    "fmt"    "net/http"    "net/http/httptest"    "testing")func TestYourHTTPGet(t *testing.T){    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {        fmt.Fprintln(w, `response from the mock server goes here`)    }))    defer ts.Close()    mockServerURL := ts.URL    status, data, err := GetData(mockServerURL)    if err != nil {        fmt.Println("Error 1: ", err)    }    fmt.Println(status)    fmt.Println(string(data))}

蝴蝶不菲

你GetData()的 return 是一个指针。你跑GetData()进去main.go,retun时,它会关闭resp.body。如果你再读一遍,它会导致http: read on closed response body所以如果你想再次阅读正文,你不应该返回*http.Response,你应该克隆resp.body返回
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go