適当なエンドポイントにアクセスして、レスポンスボディを読んで、更に他にも利用しようとします。
func badFunc() {
// httpRequestは、http.Client.GetのWrapper
resp, err := httpRequest("https://httpbin.org/")
if err != nil {
log.Fatal(err.Error())
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 中略
// スクレイピング
data, err := scrap(resp.Body)
// 以下、省略
}
一見問題なさそうにみえますが、実はこれは正常に動きません。
ioutil.ReadAll(resp.Body)
が動いた段階で、resp.Body
の中が空になってしまうからです。これによって、後続のスクレイピングの処理は空データをスクレイピングしようとするのでエラー扱いになります。
(というか、独自処理なので、エラー扱いにしますといったほうが正しいかも)
結論から先にいうと、下記のようにしてしまえば解決します。
違いは、ioutil.ReadAll(resp.Body)
の後に、resp.Body
にレスポンス入れ直しているところ。これで、後続処理も正しく動くようになります。
func goodFunc() {
// httpRequestは、http.Client.GetのWrapper
resp, err := httpRequest("https://httpbin.org/")
if err != nil {
log.Fatal(err.Error())
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 中略
// ioutil.ReadAllの時点で消失してしまうので
resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
// スクレイピング
data, err := scrap(resp.Body)
// 以下、省略
}
メモリリークしたりするとこの現象が起こるらしいですが、そんなことが起きているわけでもなさそうなので、原因はよくわかりません。ソース読んでも、Stackoverflowとか見ても解決方法は書いてても、この現象の原因みたいなのはわかりませんでした。
(わかったら追記します)
とりあえず、http.Response.Bodyちゃんはきちんとお家に帰ってきましたとさ。
めでたしめでたし