因此,我在 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>();
//...
}
不需要调用
Dispose
方法,但如果出于某种原因需要,您仍然可以调用它。
无需处置客户端。 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
的调用没有任何作用。但这并不危险。
不。您不应该处置您的客户。更一般地说,您不应该处置通过 DI 容器检索到的“任何东西”,在 ASP.NET Core 中默认是服务集合。生命周期由 DI 容器管理,因此如果您处置客户端,但稍后将其注入到某些内容中,您将得到一个 ObjectDisposedException
。让容器处理处置。
这实际上是与
IDisposable
类的常见混淆。如果您的类本身拥有依赖项,您个人应该只实现
IDisposable
。如果它的所有依赖项都是注入,那么您不应该实现
IDisposable
,因为它不拥有任何需要处置的东西。同样,您不应该处置注入到您的类中的任何内容,因为它不拥有这些依赖项。只处理你特别新买的东西。如果您没有看到关键字 new
,您可能不应该丢弃。,并且有一个代码示例引起了我的注意:
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>();
}
}
我有兴趣看看其他人对此的想法。