Подтвердить что ты не робот

Когда или если нужно удалить HttpResponseMessage при вызове ReadAsStreamAsync?

Я использую System.Net.Http.HttpClient чтобы выполнить некоторую клиентскую HTTP-связь. У меня есть все HTTP в одном месте, отвлеченные от остальной части кода. В одном случае я хочу прочитать контент ответа как поток, но потребитель потока хорошо изолирован от того, где происходит HTTP-связь, и поток открывается. В месте, ответственном за HTTP-связь, я избавляюсь от всего материала HttpClient.

Этот unit тест завершится неудачно в Assert.IsTrue(stream.CanRead):

[TestMethod]
public async Task DebugStreamedContent()
{
    Stream stream = null; // in real life the consumer of the stream is far away 
    var client = new HttpClient();        
    client.BaseAddress = new Uri("https://www.google.com/", UriKind.Absolute);

    using (var request = new HttpRequestMessage(HttpMethod.Get, "/"))
    using (var response = await client.SendAsync(request))
    {
        response.EnsureSuccessStatusCode();
        //here I would return the stream to the caller
        stream = await response.Content.ReadAsStreamAsync();
    }

    Assert.IsTrue(stream.CanRead); // FAIL if response is disposed so is the stream
}

Обычно я стараюсь избавиться от чего-либо IDisposable как можно скорее, но в этом случае утилизация HttpResponseMessage также предоставляет Stream возвращенный из ReadAsStreamAsync.

Таким образом, кажется, что код вызова должен знать и владеть ответным сообщением, а также потоком, или я оставляю сообщение ответа нераскрытым и позволяю финализатору справиться с ним. Ни один из вариантов не кажется правильным.

В этом ответе говорится о том, чтобы не HttpClient. Как насчет сообщения HttpRequestMessage и/или HttpResponseMessage?

Я что-то упускаю? Я надеюсь, что потребительский код не знает HTTP, но оставление всех этих нераскрытых объектов идет против года привычки!

4b9b3361

Ответ 1

Итак, кажется, что код вызова должен знать и принимать владение ответным сообщением, а также поток, или я ухожу ответное сообщение не задано, и пусть финализатор справится с этим. Ни один из вариантов не кажется правильным.

В этом конкретном случае нет финализаторов. Ни HttpResponseMessage, ни HttpRequestMessage не реализуют финализатор (и это хорошо!). Если вы не уничтожаете ни одного из них, они получат мусор, собранный после того, как GC заработает, и дескриптор к их основным потокам будет собран после того, как это произойдет.

Пока вы используете эти объекты, не удаляйте их. После этого утилизируйте их. Вместо того, чтобы обернуть их в оператор using, вы всегда можете явно вызвать Dispose по завершении. В любом случае потребительский код не нуждается в каких-либо знаниях, лежащих в основе http-запросов.

Ответ 2

Вы также можете принимать поток в качестве входного параметра, поэтому вызывающий имеет полный контроль над типом потока, а также его удаление. И теперь вы также можете удалить httpResponse, прежде чем элемент управления покинет этот метод.
Ниже приведен метод расширения для HttpClient

    public static async Task HttpDownloadStreamAsync(this HttpClient httpClient, string url, Stream output)
    {
        using (var httpResponse = await httpClient.GetAsync(url).ConfigureAwait(false))
        {
            // Ensures OK status
            response.EnsureSuccessStatusCode();

            // Get response stream
            var result = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);

            await result.CopyToAsync(output).ConfigureAwait(false);
            output.Seek(0L, SeekOrigin.Begin);                
        }
    }