Нужно ли закрывать объект ответа, если возникает ошибка при вызове http.Get(url)?

В следующем коде также необходимо закрыть тело ответа в случае ошибки:

res, err := http.Get(url)

if err != nil {
    log.Printf("Error: %s\n", err)
}

defer res.Body.Close()

Ответ 1

Общая концепция заключается в том, что, когда функция (или метод) имеет множество значений возврата, одна из которых является error, сначала следует проверить ошибку и продолжить, только если ошибка nil. Функции должны возвращать нулевые значения для других значений (без ошибок), если есть error. Если функция ведет себя по-другому, она должна быть документирована. http.Get() не документирует такое отклонение.

Таким образом, его следует обрабатывать следующим образом:

res, err := http.Get(url)
if err != nil {
    log.Printf("Error: %s\n", err)
    return
}

defer res.Body.Close()
// Read/work with body

Примечания:

Как также подтверждает JimB, если возвращается ошибка не nil, даже если ответ не nil, нам не нужно его закрывать. В случае ошибки перенаправления ответ не nil может содержать контекст и дополнительную информацию о том, где не удалось выполнить перенаправление. См. Подробности ниже:

http.Get() отличает общее понятие "большую часть времени": он возвращает ответ nil, если есть ошибка:

return nil, someError

Однако, проверяя client.go, невозвращенный метод Client.doFollowingRedirects(), в настоящее время строка # 427:

if redirectFailed {
    // Special case for Go 1 compatibility: return both the response
    // and an error if the CheckRedirect function failed.
    // See https://golang.org/issue/3795
    return resp, urlErr
}

Таким образом, из-за проблемы с обратной совместимостью он может возвращать ответ не nil и ошибку не nil в то же время, если сбой перенаправления.

С другой стороны, попытка вызова resp.Body.Close(), если resp равна nil, приведет к панике во время выполнения.

Итак, если мы хотим закрыть тело ответа в этом случае, оно может выглядеть так (может быть закрыто, если resp не nil):

res, err := http.Get(url)
if err != nil {
    log.Printf("Error: %s\n", err)
}
if res != nil {
    defer res.Body.Close()
    // Read/work with body
}

Или:

res, err := http.Get(url)
if err != nil {
    log.Printf("Error: %s\n", err)
}
if res == nil {
    return
}

defer res.Body.Close()
// Read/work with body

Документ http.Response гарантирует, что Response.Body не будет nil, даже если нет данных ответа:

// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body.