某些时候我们需要为HttpClient动态配置一些东西, 例如证书等, 例如服务是一个回调服务, 而被回调方采用了自定义的https(即自定义证书),本文就将讲述如何实现这种需求
比如如何使用IHttpClientFactory动态添加cer证书
有三种方法推荐方法
- 方法一: 推荐的做法是这样子
services.AddHttpClient("a业务").ConfigurePrimaryHttpMessageHandler(...a业务证书) services.AddHttpClient("b业务").ConfigurePrimaryHttpMessageHandler(...b业务证书) ServiceProvider.GetService().CreateClient("a业务")....
- 方法二:
如果你要完全自定义则可以用 new System.Net.Http.HttpClient(handler)
- 方法三:
在或者用骚操作, 替换配置的方式也可以 逻辑就是实现一个自己的HttpClientFactoryOptions, 然后动态生成它.
get_cert_handler_by_name 是你自己的方法,可以根据任何是否使用区别业务名称a,b,c new 一个handler.
但是要注意, 这样子所有从ServiceProvider获取HttpClient都会走到这个自定义配置类上面, 要做好兼容性.
class MyClass : IPostConfigureOptions { public void PostConfigure(string name, HttpClientFactoryOptions options) => options.HttpMessageHandlerBuilderActions.Add(p => p.PrimaryHandler = get_cert_handler_by_name(name)); } //注册这个服务 services.AddSingleton<Microsoft.Extensions.Options.IPostConfigureOptions, MyClass>();
上述是一些前情概要, 那么接下来我们就来实现这个需求.
秒想到一个方法, 我们可以直接new HttpClient()
, 在每一次要使用的时候都直接来一个, 简单粗暴.
秒想到第二个方法, 又或者用一个Dictionary
根据名字缓存client对象.
但是前者性能是个问题,而且涉及到端口的占用释放问题, 在调用量稍大的情况下得凉凉, 后者则是有已知的问题HttpClient对象没法感知dns的变更.
其他一些更不靠谱的方法还有: 使用代码配置方式(services.AddHttpClient("callback provider side").ConfigurePrimaryHttpMessageHandler()
)配置所有证书, 还有把所有证书都安装的本机上并设置为信任证书.
那么能除了上面这些不靠谱的方式(或者说有致命缺陷的方式), 还有靠谱的么, 那当然是有的, 例如运行时的动态配置实现方案.
所以, 接下来, 我来推荐 2 种方式式,就是我们的IHttpMessageHandlerBuilderFilter
和IPostConfigureOptions
.
官方有什么推荐么?
针对如何为HttpClient对象添加证书, 官方文档的实现是:使用证书和来自 IHttpClientFactory 的命名 HttpClient 实现 HttpClient 和 使用证书和 HttpClientHandler 实现 HttpClient, 但是在这里显然没法解决我们的运行时配置的需求, 但是它给出了一条线索, 那就是命名配置. 它可以为我们的每一个不同的provider提供自定义配置. 只要我们能为每一个不同的provider能提供运行时配置即可, 接下来就是源码阅读时间了:
下文中的所有代码都来自netcore 3.1, 并且仅copy关键代码, 完整代码可以前往github查看.
IHttpClientFactory.CreateClient是如何将HttpClient创建出来的?
- 每次
CreateClient
出来的都是一个新的HttpClient
实例 - 在
CreateHandler
中的_activeHandlers
将为我们缓存我们的handler, 默认是2分钟(定义在HttpClientFactoryOptions.HandlerLifetime
)- 这里有一个知识点就是如果我的请求刚好在过期时间前一点点获取到这个缓存的对象,就是有可能我当前的请求还在进行中, 但是2分钟过去后这个handler就要被回收的. 那官方是如何替我们解决这个可能的bug的呢, 请查看文章
来源gao!%daima.com搞$代*!码网
Cleaning up expired handlers, 我就不赘述了, 关键点在于用了一个
WeakReference
- 这里有一个知识点就是如果我的请求刚好在过期时间前一点点获取到这个缓存的对象,就是有可能我当前的请求还在进行中, 但是2分钟过去后这个handler就要被回收的. 那官方是如何替我们解决这个可能的bug的呢, 请查看文章
CreateHandlerEntry
方法则是真正的创建以及配置我们的handlers的地方.- 从
IConfiguration
获得一个HttpClientFactoryOptions
对象 - 应用
IHttpMessageHandlerBuilderFilter
- 应用
HttpMessageHandlerBuilderActions
- 从
//Microsoft.Extensions.Http.DefaultHttpClientFactory public HttpClient CreateClient(string name) { HttpClient httpClient = new HttpClient(this.CreateHandler(name), disposeHandler: false); return httpClient; } public HttpMessageHandler CreateHandler(string name) { ActiveHandlerTrackingEntry value = this._activeHandlers.GetOrAdd(name, this._entryFactory).Value; //_entryFactory可以直接理解为是CreateHandlerEntry方法.它真实的类型是Lazy(CreateHandlerEntry,LazyThreadSafetyMode.ExecutionAndPublication)的, 也就是并发安全的调用CreateHandlerEntry. return value.Handler; } internal ActiveHandlerTrackingEntry CreateHandlerEntry(string name) { HttpClientFactoryOptions options = this._optionsMonitor.Get(name); HttpMessageHandlerBuilder requiredService = provider.GetRequiredService(); requiredService.Name = name; Action action = Configure; // 扩展点二 HttpClientFactoryOptions.HttpMessageHandlerBuilderActions for (int num = this._filters.Length - 1; num >= 0; num--) { action = this._filters[num].Configure(action); //扩展点一 _filters(构造函数传入的IEnumerable<ihttp以上就是asp.net core为IHttpClientFactory添加动态命名配置的详细内容,更多请关注gaodaima搞代码网其它相关文章!