asp.net core为IHttpClientFactory添加动态命名配置

 更新时间:2021年9月22日 09:59  点击:1766

比如如何使用IHttpClientFactory动态添加cer证书

有三种方法推荐方法

  • 方法一: 推荐的做法是这样子

services.AddHttpClient("a业务").ConfigurePrimaryHttpMessageHandler(...a业务证书)
services.AddHttpClient("b业务").ConfigurePrimaryHttpMessageHandler(...b业务证书)
ServiceProvider.GetService<IHttpClientFactory>().CreateClient("a业务")....

  • 方法二:

如果你要完全自定义则可以用 new System.Net.Http.HttpClient(handler)

  • 方法三:

在或者用骚操作, 替换配置的方式也可以 逻辑就是实现一个自己的HttpClientFactoryOptions, 然后动态生成它.
get_cert_handler_by_name 是你自己的方法,可以根据任何是否使用区别业务名称a,b,c new 一个handler.
但是要注意, 这样子所有从ServiceProvider获取HttpClient都会走到这个自定义配置类上面, 要做好兼容性.

class MyClass : IPostConfigureOptions<HttpClientFactoryOptions>
        {
            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<Microsoft.Extensions.Http.HttpClientFactoryOptions>, MyClass>();

上述是一些前情概要, 那么接下来我们就来实现这个需求.

秒想到一个方法, 我们可以直接new HttpClient(), 在每一次要使用的时候都直接来一个, 简单粗暴.
秒想到第二个方法, 又或者用一个Dictionary<string,HttpClient>根据名字缓存client对象.
但是前者性能是个问题,而且涉及到端口的占用释放问题, 在调用量稍大的情况下得凉凉, 后者则是有已知的问题HttpClient对象没法感知dns的变更.

其他一些更不靠谱的方法还有: 使用代码配置方式(services.AddHttpClient("callback provider side").ConfigurePrimaryHttpMessageHandler())配置所有证书, 还有把所有证书都安装的本机上并设置为信任证书.

那么能除了上面这些不靠谱的方式(或者说有致命缺陷的方式), 还有靠谱的么, 那当然是有的, 例如运行时的动态配置实现方案.

所以, 接下来, 我来推荐 2 种方式式,就是我们的IHttpMessageHandlerBuilderFilterIPostConfigureOptions.

官方有什么推荐么?

针对如何为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的呢, 请查看文章Cleaning up expired handlers, 我就不赘述了, 关键点在于用了一个WeakReference
  • 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<HttpMessageHandlerBuilder>();
	requiredService.Name = name;
	Action<HttpMessageHandlerBuilder> action = Configure; // 扩展点二 HttpClientFactoryOptions.HttpMessageHandlerBuilderActions
	for (int num = this._filters.Length - 1; num >= 0; num--)
	{
		action = this._filters[num].Configure(action); //扩展点一 _filters(构造函数传入的IEnumerable<IHttpMessageHandlerBuilderFilter> filters).
	}
	action(requiredService);
	LifetimeTrackingHttpMessageHandler handler = new LifetimeTrackingHttpMessageHandler(requiredService.Build());
	return new ActiveHandlerTrackingEntry(name, handler, serviceScope, options.HandlerLifetime);

	void Configure(HttpMessageHandlerBuilder b)
	{
		for (int i = 0; i < options.HttpMessageHandlerBuilderActions.Count; i++)
		{
			options.HttpMessageHandlerBuilderActions[i](b);
		}
	}
}

关键点代码就是上面代码中标记出来的扩展点一 和 扩展点二.

  • 扩展点一: 需要注入适当的IHttpMessageHandlerBuilderFilter对象,就可以改写requiredService对象, 也就可以实现我们要的运行时动态配置了.
  • 扩展点二: 需要实现自定义的IConfiguration配置, 只要this._optionsMonitor.Get(name)拿到的对象的HttpMessageHandlerBuilderActions属性包含我们相应的改写代码即可.

扩展点一的实现

为HttpClient的handler增加一个配置的filter, 针对符合的handlerBuilder增加一些自己的改写逻辑.
我们在用HttpClient对象的时候产生的日志("Sending HTTP request......","Received HTTP response headers after......")就是由这个Filter特性注入的. 官方参考代码:LoggingHttpMessageHandlerBuilderFilter

个人见解: 觉得在这个扩展点加这个业务不是特别的符合应用场景, 所以我建议在扩展点二做这个事情.

class MyHttpClientHandlerFilter : IHttpMessageHandlerBuilderFilter
{
    public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
    {
        void Configure(HttpMessageHandlerBuilder builder)
        {
            next(builder); //一开始就调用next, 这样我们的整个HandlerBuilder的执行顺序就是依次call _filters, 最后call options.HttpMessageHandlerBuilderActions(扩展点二).

            if (builder.Name.StartsWith("CallbackProviderSide-")) //我们可以为这类业务统一加一个前缀做区别, 这样就不会影响其他的HttpClient对象了.
            {
                //builder.PrimaryHandler= your custom handler. 参考官方文档的实现.
            }
        }
        return Configure;
    }
}
//然后在DI容器中注入我们的filter.
ServiceCollection.AddSingleton<IHttpMessageHandlerBuilderFilter,MyHttpClientHandlerFilter>();

扩展点二的实现

class MyHttpClientCustomConfigure : IPostConfigureOptions<HttpClientFactoryOptions>
{
    public void PostConfigure(string name, HttpClientFactoryOptions options)
    {
        if (name.StartsWith("CallbackProviderSide-")) //我们可以为这类业务统一加一个前缀做区别, 这样就不会影响其他的HttpClient对象了.
        {
            options.HttpMessageHandlerBuilderActions.Add(p =>
            {
                //p.PrimaryHandler= your custom handler. 参考官方文档的实现.
            });
        }
    }
}

//然后在DI容器中注入我们的这个配置扩展类.
ServiceCollection.AddSingleton<Microsoft.Extensions.Options.IPostConfigureOptions<Microsoft.Extensions.Http.HttpClientFactoryOptions>, MyHttpClientCustomConfigure>();

为什么这里注入的类型是Microsoft.Extensions.Options.IPostConfigureOptions<Microsoft.Extensions.Http.HttpClientFactoryOptions>, 是因为OptionsFactory它的构造函数需要的就是这个. 至于有关Configuration系统的扩展和源代码在这里就不在这里展开了.

使用

至于用它就简单了

var factory = ServiceProvider.GetService<IHttpClientFactory>();
var httpClientForBaidu = factory.CreateClient("CallbackProviderSide-baidu");
var httpClientForCnblogs = factory.CreateClient("CallbackProviderSide-Cnblogs");

总结一下

这样子, 我们的这个运行时动态配置HttpClient就算完成了, 我也轻轻松松又水了一篇文章.
另外,有关IHttpClientFactory背后的故事可以查看文章Exploring the code behind IHttpClientFactory in depth, 很完整的流程图在配上代码, 把它讲解的清清楚楚.

以上就是asp.net core为IHttpClientFactory添加动态命名配置的详细内容,更多关于asp.net core 添加动态命名配置的资料请关注猪先飞其它相关文章!

[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
  • ASP.NET 2.0中的数据操作:使用两个DropDownList过滤的主/从报表

    在前面的指南中我们研究了如何显示一个简单的主/从报表, 该报表使用DropDownList和GridView控件, DropDownList填充类别,GridView显示选定类别的产品. 这类报表用于显示具有...2016-05-19
  • ASP.NET单选按钮控件RadioButton常用属性和方法介绍

    RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22
  • 详解.NET Core 使用HttpClient SSL请求出错的解决办法

    这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22
  • ASP.NET中iframe框架点击左边页面链接 右边显示链接页面内容

    这篇文章主要介绍了ASP.NET中iframe框架点击左边页面链接,右边显示链接页面内容的实现代码,感兴趣的小伙伴们可以参考一下...2021-09-22
  • 创建一个完整的ASP.NET Web API项目

    ASP.NET Web API具有与ASP.NET MVC类似的编程方式,ASP.NET Web API不仅仅具有一个完全独立的消息处理管道,而且这个管道比为ASP.NET MVC设计的管道更为复杂,功能也更为强大。下面创建一个简单的Web API项目,需要的朋友可以参考下...2021-09-22
  • ASP.NET连接MySql数据库的2个方法及示例

    这篇文章主要介绍了ASP.NET连接MySql数据库的2个方法及示例,使用的是MySQL官方组件和ODBC.NET,需要的朋友可以参考下...2021-09-22
  • Asp.Net使用Bulk实现批量插入数据

    这篇文章主要介绍了Asp.Net使用Bulk实现批量插入数据的方法,对于进行asp.net数据库程序设计非常有借鉴价值,需要的朋友可以参考下...2021-09-22
  • 在ASP.NET 2.0中操作数据之二十九:用DataList和Repeater来显示数据

    本文主要讲解ASP.NET 2.0中如何使用DataList 和 Repeater 来呈现数据,DataList包含一个table标记,而Repeater不会添加任何额外的代码,个人在实际开发中更推荐使用Repeater。...2021-09-22
  • Asp.net中获取DataTable选择第一行某一列值

    这篇文章主要介绍了获取DataTable选择第一行某一列值,需要的朋友可以参考下...2021-09-22
  • Asp.net动态生成html页面的方法分享

    这篇文章介绍了Asp.net动态生成html页面的方法,有需要的朋友可以参考一下...2021-09-22
  • ASP.Net中的async+await异步编程的实现

    这篇文章主要介绍了ASP.Net中的async+await异步编程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22