依赖注入:HttpClient 还是 HttpClientFactory?



到处我都可以看到在 DI 中创建客户端(基本、命名、类型(的三种主要方法,但我找不到是注入IHttpClientFactory还是HttpClient(两者都可能(。

Q1:请注射IHttpClientFactoryHttpClient有什么区别?

Q2:如果注入了IHttpClientFactory,我是否应该为每个呼叫使用factory.CreateClient()

总结

  • HttpClient只能注入到类型化客户端中
  • 对于其他用途,您需要IHttpClientFactory
  • 在这两种情况下,HttpClientMessageHandler的生存期都由框架管理,因此您不必担心(错误地(释放HttpClients

例子

为了直接注入HttpClient,您需要注册一个将接收客户端的特定类型化服务:

services.AddHttpClient<GithubClient>(c => c.BaseAddress = new System.Uri("https://api.github.com"));

现在我们可以将其注入到类型的GithubClient 中

public class GithubClient
{
public GithubClient(HttpClient client)
{
// client.BaseAddress is "https://api.github.com"
}
}

你不能在AnotherClient中注入HttpClient,因为它不是为了AnotherClient

public class AnotherClient
{
public AnotherClient(HttpClient client)
{
// InvalidOperationException, can't resolve HttpClient 
}
}

但是,您可以:
1. 注入IHttpClientFactory并调用CreateClient()。此客户端将BaseAddress设置为null
2. 或者将AnotherClient配置为不同类型的客户端,例如,使用不同的BaseAdress

更新

根据您的评论,您正在注册指定客户端。它仍然从IHttpClientFactory.CreateClient((方法解析,但你需要传递客户端的"名称">

注册

services.AddHttpClient("githubClient", c => c.BaseAddress = new System.Uri("https://api.github.com"));

用法

// note that we inject IHttpClientFactory
public HomeController(IHttpClientFactory factory)
{
this.defaultClient = factory.CreateClient(); // BaseAddress: null
this.namedClient = factory.CreateClient("githubClient"); // BaseAddress: "https://api.github.com"
}

可悲的是,我无法发表评论,而只能发布答案。因此,我建议您查看以下链接:

https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

关于您的问题,它或多或少归结为:

Q1-> IHttpClientFactory 处理 HttpClient 实例的连接池,这将帮助您处理链接中描述的加载和处置问题,如果 HttpClient 使用错误。

Q2-> 是的,您应该根据 Microsoft 文档使用 factory.create 客户端

是的,你应该使用工厂。CreateClient(( 用于每个调用。我的意思是你"不应该"在单一实例或作用域服务的构造函数中编写此代码。但是像下面这样不为每个调用都这样做的用法很好:看到这里,我们不会为每个调用创建 httpClient。即使您为每个调用创建一个 httpClient,也不会对性能产生太大影响,因为创建的 httpClient 的基础 HttpClientHandler 是池化和共享的。因此,每次只创建一个小的 c# 对象,只有开销。

public class BasicModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
public BasicModel(IHttpClientFactory httpClientFactory) =>
_httpClientFactory = httpClientFactory;
public IEnumerable<GitHubBranch>? GitHubBranches { get; set; }
public IEnumerable<StackOverFlowAnswer>? StackOverFlowAnswers { get; set; }
public async Task OnGet()
{
var httpRequestMessage1 = new HttpRequestMessage(
HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches")
{
Headers =
{
{ HeaderNames.Accept, "application/vnd.github.v3+json" },
{ HeaderNames.UserAgent, "HttpRequestsSample" }
}
};
var httpRequestMessage2 = new HttpRequestMessage(
HttpMethod.Get,
"https://api.stackoverflow.com/something")
{
Headers =
{
{ HeaderNames.Accept, "application/json" },
{ HeaderNames.UserAgent, "HttpRequestsSample" }
}
};
var httpClient = _httpClientFactory.CreateClient();
var httpResponseMessage1 = await httpClient.SendAsync(httpRequestMessage1);
if (httpResponseMessage1.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage1.Content.ReadAsStreamAsync();

GitHubBranches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
}
var httpResponseMessage2 = await httpClient.SendAsync(httpRequestMessage2);
if (httpResponseMessage2.IsSuccessStatusCode)
{
using var contentStream =
await httpResponseMessage2.Content.ReadAsStreamAsync();

StackOverFlowAnswers= await JsonSerializer.DeserializeAsync
<IEnumerable<StackOverFlowAnswer>>(contentStream);
}
}
}

现在来到你的第一季度,这取决于。当您将 HttpClient 注入到不是类型化客户端的服务时,该 HttpClient 实例实际上仅由 IHttpClientFactory 创建(假设您已经完成了服务。AddHttpClient((;在启动.cs("暂时"。这里的"短暂"是重要的词。因此,每当调用该构造函数时,都会创建一个新的 HttpClient 实例并将其传递给该构造函数。如果调用构造函数用于单例,则它是不正确的用法。HttpClient 注入到单例

一些重要的注意事项是:

  1. 所讨论的类的范围(生存期(是什么。如果它是瞬态的,那么注入 IHttpClientFactory 或 HttpClient 没有区别。但是,如果作用域是单例,则将 HttpClient 注入构造函数是不好的用法。
  2. 无论哪种方式,都是用于创建注入的 HttpClient 的 IHttpClientFactory,因此它是一样的。

任何你没有创建的HttpClient(使用新的HttpClient(((应该是短暂的。即使您正在创建 HttpClient 并将其用作单一实例,也可能不会遵守其在 DNS 更改中的不正确。

最新更新