由 HttpClientFactory 创建的 HttpClient 实例是否应该被释放?

问题描述 投票:0回答:3

因此,我在 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>();
    //...
}
.net-core dotnet-httpclient idisposable asp.net-core-2.1 httpclientfactory
3个回答
57
投票

不需要调用

Dispose
方法,但如果出于某种原因需要,您仍然可以调用它。

证明:HttpClient 和生命周期管理

无需处置客户端。 Disposal 取消传出请求并保证在调用 Dispose 后无法使用给定的 HttpClient 实例。 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
的调用没有任何作用。但这并不危险。


42
投票

不。您不应该处置您的客户。更一般地说,您不应该处置通过 DI 容器检索到的“任何东西”,在 ASP.NET Core 中默认是服务集合。生命周期由 DI 容器管理,因此如果您处置客户端,但稍后将其注入到某些内容中,您将得到一个 ObjectDisposedException。让容器处理处置。


这实际上是与

IDisposable

类的常见混淆。如果您的类本身拥有依赖项,您个人应该只实现

IDisposable
。如果它的所有依赖项都是
注入
,那么您不应该实现IDisposable,因为它不拥有任何需要处置的东西。同样,您不应该处置注入到您的类中的任何内容,因为它不拥有这些依赖项。只处理你特别新买的东西。如果您没有看到关键字
new
,您可能不应该丢弃。
    


0
投票
Microsoft learn stuff here

,并且有一个代码示例引起了我的注意: 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>(); }

}

我有兴趣看看其他人对此的想法。

© www.soinside.com 2019 - 2024. All rights reserved.