Нужно ли удалять экземпляры HttpClient, созданные HttpClientFactory?
Итак, я зарегистрировал именованного клиента в коллекции сервисов в моем Startup.cs:
services.AddHttpClient(someServiceName,
client => client.BaseAddress = baseAddress);
и теперь могу залить IHttpClientFactory
от моего поставщика услуг.
Используя это IHttpClientFactory
Я вызываю в воображении клиентский экземпляр:
var client = httpClientFactory.CreateClient(someServiceName)
Давным-давно необходимо было быть очень осторожным с HttpClient
случаи, так как это редко было правильно делать.
Тем не менее, теперь у нас есть HttpClientFactory
, это имеет значение больше? Должен / Может это client
распоряжаться без беспокойства? например
using (var httpClient = httpClientFactory.CreateClient(someServiceName))
using (var response = await httpClient.PostAsync(somePath, someData))
{
var content = await response.Content.ReadAsAsync<SomeResponse>();
//...
}
3 ответа
Нет. Вы не должны распоряжаться своим клиентом. Чтобы быть более общим, вы не должны распоряжаться чем-либо, полученным через контейнер DI, который в ASP.NET Core по умолчанию является набором служб. Контейнер DI управляет временем жизни, поэтому, если вы избавитесь от клиента, но позже он будет введен во что-то, вы получите ObjectDisposedException
, Пусть контейнер справится с утилизацией.
На самом деле это общая путаница с IDisposable
классы. Вы должны только лично осуществить IDisposable
если ваш класс сам владеет зависимостями. Если все его зависимости введены, вы не должны реализовывать IDisposable
, поскольку он не владеет ничем, что нуждается в утилизации. Точно так же вы не должны избавляться от чего-либо, внедренного в ваш класс, так как он не владеет этими зависимостями. Распоряжайтесь только вещами, которые вы специально осваиваете. Если вы не видите ключевое слово new
Вы, вероятно, не должны избавляться.
Вызов Dispose
метод не требуется, но вы все равно можете вызвать его, если вам нужно по каким-то причинам.
Доказательство: HttpClient и управление жизненным циклом
Избавление от клиента не требуется. Утилизация отменяет исходящие запросы и гарантирует, что данный экземпляр HttpClient не может быть использован после вызова Dispose. IHttpClientFactory отслеживает и удаляет ресурсы, используемые экземплярами HttpClient. Экземпляры HttpClient обычно можно рассматривать как объекты.NET, не требующие удаления.
Проверьте источникDefaultHttpClientFactory
:
public HttpClient CreateClient(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var handler = CreateHandler(name);
var client = new HttpClient(handler, disposeHandler: false);
var options = _optionsMonitor.Get(name);
for (var i = 0; i < options.HttpClientActions.Count; i++)
{
options.HttpClientActions[i](client);
}
return client;
}
Пример HttpMessageHandler
хранит неуправляемые ресурсы HttpClient
, В классическом сценарии HttpClient
создает экземпляр HttpMessageHandler
и утилизирует его, а сам утилизирует.
Вы можете увидеть в приведенном выше коде, что различные экземпляры HttpClient
разделяет один экземпляр HttpMessageHandler
и не распоряжается (disposeHandler: false
).
Итак, зов HttpClient.Dispose
ничего не делает. Но это не опасно.
Полагаю, я немного опоздал на вечеринку, но я просматривал здесь материалы по обучению Microsoft , и мое внимание привлек образец кода:
public sealed class TodoService
{
private readonly IHttpClientFactory _httpClientFactory = null!;
private readonly ILogger<TodoService> _logger = null!;
public TodoService(
IHttpClientFactory httpClientFactory,
ILogger<TodoService> logger) =>
(_httpClientFactory, _logger) = (httpClientFactory, logger);
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
////////////////////////////////////////////
// //
// HERE IT IS - USING WITH THE HTTP CLIENT//
// //
////////////////////////////////////////////
// Create the client
using HttpClient client = _httpClientFactory.CreateClient();
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo types
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"https://jsonplaceholder.typicode.com/todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? Array.Empty<Todo>();
}
catch (Exception ex)
{
_logger.LogError("Error getting something fun to say: {Error}", ex);
}
return Array.Empty<Todo>();
}
}
Мне было бы интересно узнать мысли других по этому поводу.