Когда или если нужно удалить 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, но оставление всех этих нераскрытых объектов идет против года привычки!
Ответы
Ответ 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);
}
}