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

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

res, err := http.Get(url)

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

defer res.Body.Close()

person Prashant    schedule 28.09.2015    source источник


Ответы (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
}

Or:

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.

Но если ошибка не nil, вам не нужно закрывать текст ответа, отличный от nil.

person icza    schedule 28.09.2015
comment
Если вы когда-нибудь получите тело ответа, которое требует закрытия при ошибке, это ошибка, которую необходимо зарегистрировать. В этом случае у вас должен быть пустой доводчик для тела, который нужен только для того, чтобы не паниковать при закрытии. - person JimB; 28.09.2015
comment
@JimB Я тоже так думал. Спасибо за подтверждение. Отредактирует ответ. - person icza; 28.09.2015
comment
Я пошел и дважды проверил клиентский код, и в единственном случае, когда ошибка перенаправления возвращает ответ, тело уже было закрыто, а соединение разорвано. Хотя это не повредит, никогда не должно быть необходимости defer resp.Body.Close() после ошибки клиента. - person JimB; 28.09.2015
comment
Как-то грустно, что есть такой частный случай. - person Drew LeSueur; 19.05.2016
comment
@DrewLeSueur Я бы не сказал, что это грустно: вам не нужно закрывать ответ и в этом особом случае (если err не является nil). - person icza; 19.05.2016