.NET Core 3.0之深入源码理解HttpClientFactory(二)

简介: .NET Core 3.0之深入源码理解HttpClientFactory(二)写在前面上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是如何创建HttpClie...

.NET Core 3.0之深入源码理解HttpClientFactory(二)
写在前面
上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是如何创建HttpClient实例和HttpMessageHandler实例的,并了解了DefaultHttpClientFactory内部维护者一个定时器和两个HttpMessageHandler对象集合,以定期清理无效的 HttpMessageHandler对象,详细的内容可以点击链接跳转,接下来我会接着前一篇文章继续展开相关讨论。

详细介绍
HttpMessageHandlerBuilder
该类是一个抽象类,起到生成器的作用,可用于用于配置HttpMessageHandler实例。HttpMessageHandlerBuilder会在ServiceCollection中被注册为Transient服务。调用方要为每个要创建的HttpMessageHandler实例检索一个新实例。实现者应该确保每个实例都只使用一次。

HttpMessageHandlerBuilder里面有三个比较重要的属性:

1: ///
2: /// 主HttpMessageHandler实例
3: ///
4: public abstract HttpMessageHandler PrimaryHandler { get; set; }
5:
6: ///
7: /// 这个是一个附加实例,用于配置HttpClient管道
8: ///
9: public abstract IList AdditionalHandlers { get; }
10:
11: ///
12: /// 可用于从依赖项注入容器解析服务的IServiceProvider
13: ///
14: public virtual IServiceProvider Services { get; }
这三个属性意味着每个HttpMessageHandlerBuilder都需要维护自身的HttpMessageHandler实例和管道。

其内部还有一个抽象方法:

1: public abstract HttpMessageHandler Build();
当然,内部最核心的方法就是管道的创建过程了,需要传入主派生类自身的HttpMessageHandler和管道列表对象。它会将primaryHandler实例付给管道列表的第一个Item的InnerHandler,其他对象会依此后移,这也为我们自定义HttpMessageHandler(各种中间件)提供了无限可能。

相关实现如下:

1: var next = primaryHandler;
2: for (var i = additionalHandlersList.Count - 1; i >= 0; i--)
3: {
4: var handler = additionalHandlersList[i];
5: if (handler == null)
6: {
7: var message = Resources.FormatHttpMessageHandlerBuilder_AdditionalHandlerIsNull(nameof(additionalHandlers));
8: throw new InvalidOperationException(message);
9: }
10:
11: if (handler.InnerHandler != null)
12: {
13: var message = Resources.FormatHttpMessageHandlerBuilder_AdditionHandlerIsInvalid(
14: nameof(DelegatingHandler.InnerHandler),
15: nameof(DelegatingHandler),
16: nameof(HttpMessageHandlerBuilder),
17: Environment.NewLine,
18: handler);
19: throw new InvalidOperationException(message);
20: }
21:
22: handler.InnerHandler = next;
23: next = handler;
24: }
接下来我们看一下HttpMessageHandlerBuilder一个派生类DefaultHttpMessageHandlerBuilder,其构造函数会传入IServiceProvider实例,我们的自定义操作也可以参照这个类。

关于Build方法的实现如下,比较简单主要是调用了CreateHandlerPipeline方法:

1: public override HttpMessageHandler Build()
2: {
3: if (PrimaryHandler == null)
4: {
5: var message = Resources.FormatHttpMessageHandlerBuilder_PrimaryHandlerIsNull(nameof(PrimaryHandler));
6: throw new InvalidOperationException(message);
7: }
8:
9: return CreateHandlerPipeline(PrimaryHandler, AdditionalHandlers);
10: }
ITypedHttpClientFactory
这是一个抽象工厂,该组件可以使用给定逻辑名称的自定义配置创建类型化HttpClient实例,与命名方式创建HttpClient具有相同的的功能。类型化客户端可能用于单个后端终结点,并封装此终结点的所有处理逻辑。 另一个优势是它们使用 DI 被注入到应用中需要的位置,下一篇文章会再次讨论相关功能。

我们首先看一下调用方式:

1: public static IHttpClientBuilder AddHttpClient(this IServiceCollection services)
2: where TClient : class
3: {
4: if (services == null)
5: {
6: throw new ArgumentNullException(nameof(services));
7: }
8:
9: AddHttpClient(services);
10:
11: var name = TypeNameHelper.GetTypeDisplayName(typeof(TClient), fullName: false);
12: var builder = new DefaultHttpClientBuilder(services, name);
13: builder.AddTypedClient();
14: return builder;
15: }
可以看出此处的调用与普通的HttpClient没有什么太大区别,只是增加了一个泛型标记,而且该类型没有特殊的要求,只要是个类就行。其内部依然调用AddHttpClient(services),但它调用了另一个扩展方法,如下所示:

1: public static IHttpClientBuilder AddTypedClient(this IHttpClientBuilder builder)
2: where TClient : class
3: {
4: if (builder == null)
5: {
6: throw new ArgumentNullException(nameof(builder));
7: }
8:
9: builder.Services.AddTransient(s =>
10: {
11: var httpClientFactory = s.GetRequiredService();
12: var httpClient = httpClientFactory.CreateClient(builder.Name);
13:
14: var typedClientFactory = s.GetRequiredService>();
15: return typedClientFactory.CreateClient(httpClient);
16: });
17:
18: return builder;
19: }
可以看到最终的代码调用了ITypedHttpClientFactory的CreateClient方法,Microsoft.Extensions.Http包中有一个默认的ITypedHttpClientFactory派生类,DefaultTypedHttpClientFactory,该类提供了了构造函数用于接收IServiceProvider实例,以及一个内部类声明的缓存对象,该对象十分重要,它被注册为singleton类型,已达到全局使用,并可以充当相关实例激活时的对象池。它也允许它的外部类注册为transient,这样它就不会在应用根服务提供程序上被关掉了。

相关代码如下:

1: public TClient CreateClient(HttpClient httpClient)
2: {
3: if (httpClient == null)
4: {
5: throw new ArgumentNullException(nameof(httpClient));
6: }
7:
8: return (TClient)_cache.Activator(_services, new object[] { httpClient });
9: }
内部缓存对象:

1: public class Cache
2: {
3: private readonly static Func _createActivator = () => ActivatorUtilities.CreateFactory(typeof(TClient), new Type[] { typeof(HttpClient), });
4:
5: private ObjectFactory _activator;
6: private bool _initialized;
7: private object _lock;
8:
9: public ObjectFactory Activator => LazyInitializer.EnsureInitialized(
10: ref _activator,
11: ref _initialized,
12: ref _lock,
13: _createActivator);
14: }
最后我们看一下源码中提供的范例:

1: class ExampleClient
2: {
3: private readonly HttpClient _httpClient;
4: private readonly ILogger _logger;
5: // typed clients can use constructor injection to access additional services
6: public ExampleClient(HttpClient httpClient, ILogger logger)
7: {
8: _httpClient = httpClient;
9: _logger = logger;
10: }
11: // typed clients can expose the HttpClient for application code to call directly
12: public HttpClient HttpClient => _httpClient;
13: // typed clients can also define methods that abstract usage of the HttpClient
14: public async Task SendHelloRequest()
15: {
16: var response = await _httpClient.GetAsync("/helloworld");
17: response.EnsureSuccessStatusCode();
18: }
19: }
20: //This sample shows how to consume a typed client from an ASP.NET Core middleware.
21: public void Configure(IApplicationBuilder app, ExampleClient exampleClient)
22: {
23: app.Run(async (context) =>
24: {
25: var response = await _exampleClient.GetAsync("/helloworld");
26: await context.Response.WriteAsync("Remote server said: ");
27: await response.Content.CopyToAsync(context.Response.Body);
28: });
29: }
30: //This sample shows how to consume a typed client from an ASP.NET Core MVC Controller.
31: public class HomeController : ControllerBase(IApplicationBuilder app, ExampleClient exampleClient)
32: {
33: private readonly ExampleClient _exampleClient;
34: public HomeController(ExampleClient exampleClient)
35: {
36: _exampleClient = exampleClient;
37: }
38: public async Task Index()
39: {
40: var response = await _exampleClient.GetAsync("/helloworld");
41: var text = await response.Content.ReadAsStringAsync();
42: return Content("Remote server said: " + text, "text/plain");
43: };
44: }
以上为本篇文章的主要内容,希望大家多提意见,如果喜欢记得点个推荐哦

作者: 艾心

出处: https://www.cnblogs.com/edison0621/

相关文章
|
15天前
|
开发框架 前端开发 .NET
LIMS(实验室)信息管理系统源码、有哪些应用领域?采用C# ASP.NET dotnet 3.5 开发的一套实验室信息系统源码
集成于VS 2019,EXT.NET前端和ASP.NET后端,搭配MSSQL 2018数据库。系统覆盖样品管理、数据分析、报表和项目管理等实验室全流程。应用广泛,包括生产质检(如石化、制药)、环保监测、试验研究等领域。随着技术发展,现代LIMS还融合了临床、电子实验室笔记本和SaaS等功能,以满足复杂多样的实验室管理需求。
26 3
LIMS(实验室)信息管理系统源码、有哪些应用领域?采用C# ASP.NET dotnet 3.5 开发的一套实验室信息系统源码
|
9天前
|
开发框架 前端开发 .NET
asp.net core 使用 AccessControlHelper 控制访问权限
asp.net core 使用 AccessControlHelper 控制访问权限
|
21天前
|
Cloud Native API C#
C#的现代化:.NET Core引领的技术革命
【6月更文挑战第9天】`.NET Core引领C#现代化,实现跨平台革命,提升性能并支持云原生应用。异步编程模型优化体验,统一API简化开发流程。C#应用场景扩展,开发效率提高,技术创新加速,预示其未来在技术领域将持续发挥关键作用。`
29 10
|
2天前
|
开发框架 .NET Nacos
使用 Nacos 在 C# (.NET Core) 应用程序中实现高效配置管理和服务发现
使用 Nacos 在 C# (.NET Core) 应用程序中实现高效配置管理和服务发现
9 0
|
3天前
|
存储 JSON NoSQL
技术心得记录:在.NETCore中使用CSRedis
技术心得记录:在.NETCore中使用CSRedis
|
4天前
|
SQL 机器学习/深度学习 缓存
心得经验总结:深入Dapper.NET源码(文长)
心得经验总结:深入Dapper.NET源码(文长)
|
4天前
|
SQL 开发框架 .NET
(20)ASP.NET Core EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性)
(20)ASP.NET Core EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性)
|
26天前
|
XML 开发框架 人工智能
C#/.NET/.NET Core拾遗补漏合集(24年5月更新)
C#/.NET/.NET Core拾遗补漏合集(24年5月更新)
|
26天前
|
开发框架 .NET API
ASP.NET Core Web中使用AutoMapper进行对象映射
ASP.NET Core Web中使用AutoMapper进行对象映射
|
25天前
|
开发框架 .NET Linux
【.NET Developer】已发布好的.NET Core项目文件如何打包为Docker镜像文件
该文介绍了如何不使用VS2019手动创建ASP.NET Core Blazor项目的Dockerfile并构建Docker镜像。首先,创建名为Dockerfile的文件,并复制提供的Dockerfile内容,该文件指定了基础镜像和工作目录。然后,通过CMD在项目目录下运行`docker build -t 自定义镜像名 .`来生成镜像。最后,使用`docker run`命令启动容器并验证项目运行。此外,文章还提到了将镜像推送到Azure Container Registry (ACR)的步骤。