【问题标题】:http: read on closed response body - httptest.NewServerhttp:阅读已关闭的响应正文 - httptest.NewServer
【发布时间】:2021-11-24 21:29:12
【问题描述】:

我正在尝试使用 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。如果我从main.go 中删除defer resp.Body.Close(),则测试正确通过。

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

【问题讨论】:

  • GetData 函数将正文读取到 EOF 并关闭正文。如果您希望调用者读取并关闭正文,请删除该代码。另一种选择是将responseBody 返回给调用者。

标签: go


【解决方案1】:

正如@Cerise Limón 所说,您致电resp.Body.Close() 两次,然后尝试读取封闭的正文。要修复您的代码,您可以从 GetData 函数中删除正文处理并在 GetData 之外进行处理,或者返回正文并且不要在测试中读取它。

main.go:

package main

import (
    "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 main

import (
    "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))
}

【讨论】:

    【解决方案2】:

    GetData() 的返回值是一个指针。你在main.go中运行GetData(),retun时会关闭resp.body。如果你再读一遍,它会导致http: read on closed response body

    所以如果你想再次阅读正文,你不应该返回*http.Response,你应该克隆resp.body返回

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-03
      • 2013-07-09
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 2021-04-12
      相关资源
      最近更新 更多