在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory
到目前为止,我们一直直接使用HttpClient。在每个服务中,我们都创建了一个HttpClient实例和所有必需的配置。这会导致了重复代码。在这篇文章中,我们将学习如何通过使用HttpClientFactory来改善它。当然,这并不是使用HttpClientFactory的唯一优势。我们将学习HttpClientFactory如何防止HttpClient可能导致的其他问题。此外,我们将展示如何使用HttpClientFactory创建命名和类型化客户端。
HttpClient问题
HttpClient类实现了IDisposable接口。看到这一点,尝试在using指令中使用我们的HttpClient实例,从而在它超出作用域时释放。但是,这并不是一个好的做法。如果我们丢弃了HttpClient,也将丢弃底层的HttpClientHandler。现在,这意味着对于每个新请求,必须创建一个新的HttpClient实例,从而也创建一个处理程序。当然,这就是问题所在。重新打开连接可能会导致性能变慢,因为在使用HttpClient时,这些连接和HttpClientHandler非常昂贵。
此外,还有另一个问题。通过创建太多的连接,可能会面临套接字耗尽,因为过快地使用了太多的套接字,而且没有套接字来创建新的连接。
因此,考虑到所有这些,我们不应该丢弃HttpClient,而是在整个请求中共享它。这就是我们在以前的文章中对静态HttpClient实例所做的事情。这也允许重用底层连接。
但是,我们必须注意,使用静态实例并不是最终的解决方案。当重用实例时,我们也重用连接,直到套接字关闭。为了帮助我们解决这些问题,我们可以使用HttpClientFactory来创建HttpClient实例。
HttpClientFactory如何帮助我们解决上述问题?
HttpClientFactory不仅可以创建和管理新的HttpClient实例,而且还可以与底层处理程序一起工作。当创建新的HttpClient实例时,它不会重新创建消息处理器,而是从池中获取一个。然后,它使用该消息处理程序将请求发送到API。处理程序的默认生存期设置为两分钟,在这段时间内,对新HttpClient的任何请求都可以重用现有的消息处理程序和连接。这意味着我们不必为每个请求创建一个新的消息处理程序,也不必打开一个新的连接,从而防止套接字耗尽问题。
除了解决这些问题,使用HttpClientHandler,我们还可以集中HttpClient的配置。如果你阅读本系列的前几篇文章,会发现我们必须在每个服务类中重复相同的配置。有了HttpClientHandler,我们可以改善这个问题。让我们看看如何使用HttpClientFactory。
添加HttpClientFactory
为了能够在我们的应用程序中使用HttpClientFactory,必须安装 Microsoft.Extensions.Http。
Install-Package Microsoft.Extensions.Http -Version 5.0.0
然后,我们必须使用Program 类中通过AddHttpClient方法将IHttpClientFactory和其他服务添加到服务集合中:
private static void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient(); //services.AddScoped<IHttpClientServiceImplementation, HttpClientCrudService>();
//services.AddScoped<IHttpClientServiceImplementation, HttpClientPatchService>();
//services.AddScoped<IHttpClientServiceImplementation, HttpClientStreamService>();
//services.AddScoped<IHttpClientServiceImplementation, HttpClientCancellationService>();
}
我们很快就会用额外的配置来扩展这个方法。现在,让我们创建一个新的服务类,就像我们在前面的文章中所做的那样:
public class HttpClientFactoryService : IHttpClientServiceImplementation
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly JsonSerializerOptions _options; public HttpClientFactoryService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
} public async Task Execute()
{
throw new NotImplementedException();
}
}
为了能够在我们的HttpClientFactoryService类中使用HttpClientFactory,我们必须通过依赖注入来注入它。此外,我们还为JSON序列化配置选项。我们不想在这里添加取消逻辑,所以没有像上一篇文章中那样使用CancellationTokenSource。
现在,让我们添加一个新方法来从API中获取公司数据:
private async Task GetCompaniesWithHttpClientFactory()
{
var httpClient = _httpClientFactory.CreateClient(); using (var response = await httpClient.GetAsync("https://localhost:5001/api/companies", HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode(); var stream = await response.Content.ReadAsStreamAsync(); var companies = await JsonSerializer.DeserializeAsync<List<CompanyDto>>(stream, _options);
}
}
在这段代码中,我们唯一不熟悉的部分是使用HttpClientFactory中的CreateClient方法来使用默认配置创建一个新的HttpClient。本系列前面的文章中已经解释了其他所有内容。另外,由于没有提供自定义配置,我们必须在GetAsync方法中使用完整的URI。
在此之后,我们可以修改Execute方法:
public async Task Execute(){ await GetCompaniesWithHttpClientFactory();}
同样,让我们在Program类中注册这个服务:
private static void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient(); ...
services.AddScoped<IHttpClientServiceImplementation, HttpClientFactoryService>();
}
在新方法中放置断点并启动两个应用程序:
使用命名的HttpClient实例
在Program类中,我们使用AddHttpClient方法注册IHttpClientFactory,而不需要额外的配置。这意味着用CreateClient方法创建的每个HttpClient实例都将具有相同的配置。但通常,这是不够的,因为我们的客户端应用程序在与一个或多个api通信时经常需要不同的HttpClient实例。为了支持这一点,我们可以使用命名的HttpClient实例。
在之前的文章中,我们在每个服务中使用了相同的配置来设置基址、超时和清除默认请求头。现在,我们也可以这样做,但只有一个地方:
private static void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("CompaniesClient", config =>
{
config.BaseAddress = new Uri("https://localhost:5001/api/");
config.Timeout = new TimeSpan(0, 0, 30);
config.DefaultRequestHeaders.Clear();
}); ...
services.AddScoped<IHttpClientServiceImplementation, HttpClientFactoryService>();
}
通过这些修改,AddHttpClient方法将IHttpClientFactory添加到服务集合中,并配置一个已命名的HttpClient实例。我们为实例提供一个名称和一个默认配置。在此之后,可以在我们的新服务中修改方法:
private async Task GetCompaniesWithHttpClientFactory()
{
var httpClient = _httpClientFactory.CreateClient("CompaniesClient"); using (var response = await httpClient.GetAsync("companies", HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode(); var stream = await response.Content.ReadAsStreamAsync(); var companies = await JsonSerializer.DeserializeAsync<List<CompanyDto>>(stream, _options);
}
}
我们将name参数传递给CreateClient方法,而且不必在GetAsync方法中使用完整的URI。由于使用的是客户机的名称,因此应用与此名称对应的配置。
一旦我们启动这两个应用程序,将得到和之前一样的结果:
使用类型化HttpClient实例
使用类型化实例,我们可以实现与命名实例相同的功能,但在注册过程中不必使用字符串——可以使用类型。首先在客户端应用程序中创建一个新的Clients文件夹,并在该文件夹中创建一个新的CompaniesClient类:
public class CompaniesClient
{
public HttpClient Client { get; } public CompaniesClient(HttpClient client)
{
Client = client; Client.BaseAddress = new Uri("https://localhost:5001/api/");
Client.Timeout = new TimeSpan(0, 0, 30);
Client.DefaultRequestHeaders.Clear();
}
}
这是我们使用默认配置的类型化客户端类,我们可以通过在ConfigureServices方法中再次调用AddHttpClient来在程序类中注册它:
services.AddHttpClient<CompaniesClient>();
因此,我们使用的不是客户端的名称,而是客户端的类型。
现在,在我们的HttpClientFactoryService中,必须注入新的客户端:
private readonly IHttpClientFactory _httpClientFactory;
private readonly CompaniesClient _companiesClient;
private readonly JsonSerializerOptions _options; public HttpClientFactoryService(IHttpClientFactory httpClientFactory, CompaniesClient companiesClient)
{
_httpClientFactory = httpClientFactory;
_companiesClient = companiesClient; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
然后,我们将创建一个新方法来使用类型化客户端:
private async Task GetCompaniesWithTypedClient()
{
using (var response = await _companiesClient.Client.GetAsync("companies", HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode(); var stream = await response.Content.ReadAsStreamAsync(); var companies = await JsonSerializer.DeserializeAsync<List<CompanyDto>>(stream, _options);
}
}
我们不是通过使用CreateClient方法来创建一个新的客户端实例。这一次,我们只使用注入类型的客户机及其client属性。最后,执行这个方法:
public async Task Execute()
{
//await GetCompaniesWithHttpClientFactory();
await GetCompaniesWithTypedClient();
}
现在让我们看看如何将相关的逻辑提取到CompaniesClient 类。
封装与类型化客户端相关的逻辑
因为我们已经有了类型化的客户端类,所以我们可以将服务中的所有相关逻辑提取到这个类中。为此,我们将修改CompaniesClient 类:
public class CompaniesClient
{
private readonly HttpClient _client;
private readonly JsonSerializerOptions _options; public CompaniesClient(HttpClient client)
{
_client = client; _client.BaseAddress = new Uri("https://localhost:5001/api/");
_client.Timeout = new TimeSpan(0, 0, 30);
_client.DefaultRequestHeaders.Clear(); _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
}
我们有一个私有的只读变量,将在该类中使用它来执行HttpClient的逻辑。此外,我们还添加了JsonSerializerOptions配置。现在,可以添加一个新方法:
public async Task<List<CompanyDto>> GetCompanies()
{
using (var response = await _client.GetAsync("companies", HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode(); var stream = await response.Content.ReadAsStreamAsync(); var companies = await JsonSerializer.DeserializeAsync<List<CompanyDto>>(stream, _options); return companies;
}
}
使用这个方法,我们从API中获取公司数据并返回结果。最后,可以修改服务类中的GetCompaniesWithTypedClient方法:
private async Task GetCompaniesWithTypedClient() => await _companiesClient.GetCompanies();
结论
综上所述,在本文中,我们了解到:
- HttpClientFactory解决了哪些问题
- 如何在我们的应用程序中使用HttpClientFactory
- 使用命名和类型化实例的方法
- 如何从服务中提取逻辑到客户端类
原文链接:https://code-maze.com/using-httpclientfactory-in-asp-net-core-applications/
在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory的更多相关文章
- 在ASP.NET Core中用HttpClient(一)——获取数据和内容
在本文中,我们将学习如何在ASP.NET Core中集成和使用HttpClient.在学习不同HttpClient功能的同时使用Web API的资源.如何从Web API获取数据,以及如何直接使用Ht ...
- 在ASP.NET Core中用HttpClient(二)——发送POST, PUT和DELETE请求
在上一篇文章中,我们已经学习了如何在ASP.NET Core中使用HttpClient从Web API获取数据.此外,我们还学习了如何使用GetAsync方法和HttpRequestMessage类发 ...
- 在ASP.NET Core中用HttpClient(三)——发送HTTP PATCH请求
在前面的两篇文章中,我们讨论了很多关于使用HttpClient进行CRUD操作的基础知识.如果你已经读过它们,你就知道如何使用HttpClient从API中获取数据,并使用HttpClient发送PO ...
- 在ASP.NET Core中用HttpClient(四)——提高性能和优化内存
到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容.但是我们可以通过使用流提高性能和优化内存.因此,在本文中,我们将学习如何在请求和响应中使用HttpClient流. 什么是流 流是以文件. ...
- 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求
用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...
- C# ASP.NET Core使用HttpClient的同步和异步请求
引用 Newtonsoft.Json // Post请求 public string PostResponse(string url,string postData,out string status ...
- ASP.NET CORE系列【六】Entity Framework Core 之数据迁移
原文:ASP.NET CORE系列[六]Entity Framework Core 之数据迁移 前言 最近打算用.NET Core写一份简单的后台系统,来练练手 然后又用到了Entity Framew ...
- ASP.NET Core 2.1 中的 HttpClientFactory (Part 1) HttpClientFactory介绍
原文:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore 发表于:2018年1月 ASP.NET ...
- ASP.NET MVC Model绑定(六)
ASP.NET MVC Model绑定(六) 前言 前面的篇幅对于IValueProvider的使用做个基础的示例讲解,但是没并没有对 IValueProvider类型的实现做详细的介绍,然而MVC框 ...
随机推荐
- 如何使用 js 实现tooltips 的 hover 时候坐标的自动计算
如何使用 js 实现tooltips 的 hover 时候坐标的自动计算 js 监听 mouseover event https://developer.mozilla.org/zh-CN/docs/ ...
- DNS & HTTPS bug
DNS & HTTPS bug SSL protocol version bug https://typescript-4.x-tutorials.xgqfrms.xyz/ errors Th ...
- PPT online viewer
PPT online viewer PPT 在线查看器 SpeakerDeck https://speakerdeck.com/xgqfrms/python?slide=3 SlideShare ht ...
- 基于NGK发行的稳定币USDN如何撼动市场?
近日,基于NGK发行的稳定币USDN在各大社区的热度越来越高,很多人都说USDN将会撼动市场,那么USDN究竟有怎样的优势,能引起这么大的轰动呢?今天我们就一起来分析一下USDN. USDN是基于公链 ...
- C++算法代码——奖学金
题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1098 题目描述 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学 ...
- JPEG解码——(4)霍夫曼解码
本篇是该系列的第四篇,主要介绍霍夫曼解码相关内容. 承接上篇,文件头解析完毕后,就进入了编码数据区域,即SOS的tag后的区域,也是图片数据量的大头所在. 1. 解码过程规则描述 a)从此颜色分量单元 ...
- 1095 Cars on Campus——PAT甲级真题
1095 Cars on Campus Zhejiang University has 6 campuses and a lot of gates. From each gate we can col ...
- C++Template 模版的本质
我想知道上帝的構思,其他的都祇是細節. ...
- Docker-compose编排微服务顺序启动
一.概述 docker-compose可以方便组合多个 docker 容器服务, 但是, 当容器服务之间存在依赖关系时, docker-compose 并不能保证服务的启动顺序.docker-comp ...
- STL栈
stack的基本操作有: 1.入栈:如s.push(x); 2.出栈:如s.pop(). 注意:出栈操作只是删除栈顶的元素,并不返回该元素. 3.访问栈顶:如s.top(); 4.判断栈空:如s.em ...