asp.net core為IHttpClientFactory添加動(dòng)態(tài)命名配置
比如如何使用IHttpClientFactory動(dòng)態(tài)添加cer證書
有三種方法推薦方法
- 方法一: 推薦的做法是這樣子
services.AddHttpClient("a業(yè)務(wù)").ConfigurePrimaryHttpMessageHandler(...a業(yè)務(wù)證書)
services.AddHttpClient("b業(yè)務(wù)").ConfigurePrimaryHttpMessageHandler(...b業(yè)務(wù)證書)
ServiceProvider.GetService<IHttpClientFactory>().CreateClient("a業(yè)務(wù)")....
- 方法二:
如果你要完全自定義則可以用 new System.Net.Http.HttpClient(handler)
- 方法三:
在或者用騷操作, 替換配置的方式也可以 邏輯就是實(shí)現(xiàn)一個(gè)自己的HttpClientFactoryOptions, 然后動(dòng)態(tài)生成它.
get_cert_handler_by_name 是你自己的方法,可以根據(jù)任何是否使用區(qū)別業(yè)務(wù)名稱a,b,c new 一個(gè)handler.
但是要注意, 這樣子所有從ServiceProvider獲取HttpClient都會(huì)走到這個(gè)自定義配置類上面, 要做好兼容性.
class MyClass : IPostConfigureOptions<HttpClientFactoryOptions>
{
public void PostConfigure(string name, HttpClientFactoryOptions options)
=> options.HttpMessageHandlerBuilderActions.Add(p => p.PrimaryHandler = get_cert_handler_by_name(name));
}
//注冊(cè)這個(gè)服務(wù)
services.AddSingleton<Microsoft.Extensions.Options.IPostConfigureOptions<Microsoft.Extensions.Http.HttpClientFactoryOptions>, MyClass>();
上述是一些前情概要, 那么接下來我們就來實(shí)現(xiàn)這個(gè)需求.
秒想到一個(gè)方法, 我們可以直接new HttpClient(), 在每一次要使用的時(shí)候都直接來一個(gè), 簡(jiǎn)單粗暴.
秒想到第二個(gè)方法, 又或者用一個(gè)Dictionary<string,HttpClient>根據(jù)名字緩存client對(duì)象.
但是前者性能是個(gè)問題,而且涉及到端口的占用釋放問題, 在調(diào)用量稍大的情況下得涼涼, 后者則是有已知的問題HttpClient對(duì)象沒法感知dns的變更.
其他一些更不靠譜的方法還有: 使用代碼配置方式(services.AddHttpClient("callback provider side").ConfigurePrimaryHttpMessageHandler())配置所有證書, 還有把所有證書都安裝的本機(jī)上并設(shè)置為信任證書.
那么能除了上面這些不靠譜的方式(或者說有致命缺陷的方式), 還有靠譜的么, 那當(dāng)然是有的, 例如運(yùn)行時(shí)的動(dòng)態(tài)配置實(shí)現(xiàn)方案.
所以, 接下來, 我來推薦 2 種方式式,就是我們的IHttpMessageHandlerBuilderFilter和IPostConfigureOptions.
官方有什么推薦么?
針對(duì)如何為HttpClient對(duì)象添加證書, 官方文檔的實(shí)現(xiàn)是:使用證書和來自 IHttpClientFactory 的命名 HttpClient 實(shí)現(xiàn) HttpClient 和 使用證書和 HttpClientHandler 實(shí)現(xiàn) HttpClient, 但是在這里顯然沒法解決我們的運(yùn)行時(shí)配置的需求, 但是它給出了一條線索, 那就是命名配置. 它可以為我們的每一個(gè)不同的provider提供自定義配置. 只要我們能為每一個(gè)不同的provider能提供運(yùn)行時(shí)配置即可, 接下來就是源碼閱讀時(shí)間了:
下文中的所有代碼都來自netcore 3.1, 并且僅copy關(guān)鍵代碼, 完整代碼可以前往github查看.
IHttpClientFactory.CreateClient是如何將HttpClient創(chuàng)建出來的?
- 每次
CreateClient出來的都是一個(gè)新的HttpClient實(shí)例 - 在
CreateHandler中的_activeHandlers將為我們緩存我們的handler, 默認(rèn)是2分鐘(定義在HttpClientFactoryOptions.HandlerLifetime)- 這里有一個(gè)知識(shí)點(diǎn)就是如果我的請(qǐng)求剛好在過期時(shí)間前一點(diǎn)點(diǎn)獲取到這個(gè)緩存的對(duì)象,就是有可能我當(dāng)前的請(qǐng)求還在進(jìn)行中, 但是2分鐘過去后這個(gè)handler就要被回收的. 那官方是如何替我們解決這個(gè)可能的bug的呢, 請(qǐng)查看文章Cleaning up expired handlers, 我就不贅述了, 關(guān)鍵點(diǎn)在于用了一個(gè)
WeakReference
- 這里有一個(gè)知識(shí)點(diǎn)就是如果我的請(qǐng)求剛好在過期時(shí)間前一點(diǎn)點(diǎn)獲取到這個(gè)緩存的對(duì)象,就是有可能我當(dāng)前的請(qǐng)求還在進(jìn)行中, 但是2分鐘過去后這個(gè)handler就要被回收的. 那官方是如何替我們解決這個(gè)可能的bug的呢, 請(qǐng)查看文章Cleaning up expired handlers, 我就不贅述了, 關(guān)鍵點(diǎn)在于用了一個(gè)
CreateHandlerEntry方法則是真正的創(chuàng)建以及配置我們的handlers的地方.- 從
IConfiguration獲得一個(gè)HttpClientFactoryOptions對(duì)象 - 應(yīng)用
IHttpMessageHandlerBuilderFilter - 應(yīng)用
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方法.它真實(shí)的類型是Lazy<>(CreateHandlerEntry,LazyThreadSafetyMode.ExecutionAndPublication)的, 也就是并發(fā)安全的調(diào)用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; // 擴(kuò)展點(diǎn)二 HttpClientFactoryOptions.HttpMessageHandlerBuilderActions
for (int num = this._filters.Length - 1; num >= 0; num--)
{
action = this._filters[num].Configure(action); //擴(kuò)展點(diǎn)一 _filters(構(gòu)造函數(shù)傳入的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);
}
}
}
關(guān)鍵點(diǎn)代碼就是上面代碼中標(biāo)記出來的擴(kuò)展點(diǎn)一 和 擴(kuò)展點(diǎn)二.
- 擴(kuò)展點(diǎn)一: 需要注入適當(dāng)?shù)腎HttpMessageHandlerBuilderFilter對(duì)象,就可以改寫
requiredService對(duì)象, 也就可以實(shí)現(xiàn)我們要的運(yùn)行時(shí)動(dòng)態(tài)配置了. - 擴(kuò)展點(diǎn)二: 需要實(shí)現(xiàn)自定義的IConfiguration配置, 只要
this._optionsMonitor.Get(name)拿到的對(duì)象的HttpMessageHandlerBuilderActions屬性包含我們相應(yīng)的改寫代碼即可.
擴(kuò)展點(diǎn)一的實(shí)現(xiàn)
為HttpClient的handler增加一個(gè)配置的filter, 針對(duì)符合的handlerBuilder增加一些自己的改寫邏輯.
我們?cè)谟肏ttpClient對(duì)象的時(shí)候產(chǎn)生的日志("Sending HTTP request......","Received HTTP response headers after......")就是由這個(gè)Filter特性注入的. 官方參考代碼:LoggingHttpMessageHandlerBuilderFilter
個(gè)人見解: 覺得在這個(gè)擴(kuò)展點(diǎn)加這個(gè)業(yè)務(wù)不是特別的符合應(yīng)用場(chǎng)景, 所以我建議在擴(kuò)展點(diǎn)二做這個(gè)事情.
class MyHttpClientHandlerFilter : IHttpMessageHandlerBuilderFilter
{
public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
{
void Configure(HttpMessageHandlerBuilder builder)
{
next(builder); //一開始就調(diào)用next, 這樣我們的整個(gè)HandlerBuilder的執(zhí)行順序就是依次call _filters, 最后call options.HttpMessageHandlerBuilderActions(擴(kuò)展點(diǎn)二).
if (builder.Name.StartsWith("CallbackProviderSide-")) //我們可以為這類業(yè)務(wù)統(tǒng)一加一個(gè)前綴做區(qū)別, 這樣就不會(huì)影響其他的HttpClient對(duì)象了.
{
//builder.PrimaryHandler= your custom handler. 參考官方文檔的實(shí)現(xiàn).
}
}
return Configure;
}
}
//然后在DI容器中注入我們的filter.
ServiceCollection.AddSingleton<IHttpMessageHandlerBuilderFilter,MyHttpClientHandlerFilter>();
擴(kuò)展點(diǎn)二的實(shí)現(xiàn)
class MyHttpClientCustomConfigure : IPostConfigureOptions<HttpClientFactoryOptions>
{
public void PostConfigure(string name, HttpClientFactoryOptions options)
{
if (name.StartsWith("CallbackProviderSide-")) //我們可以為這類業(yè)務(wù)統(tǒng)一加一個(gè)前綴做區(qū)別, 這樣就不會(huì)影響其他的HttpClient對(duì)象了.
{
options.HttpMessageHandlerBuilderActions.Add(p =>
{
//p.PrimaryHandler= your custom handler. 參考官方文檔的實(shí)現(xiàn).
});
}
}
}
//然后在DI容器中注入我們的這個(gè)配置擴(kuò)展類.
ServiceCollection.AddSingleton<Microsoft.Extensions.Options.IPostConfigureOptions<Microsoft.Extensions.Http.HttpClientFactoryOptions>, MyHttpClientCustomConfigure>();
為什么這里注入的類型是Microsoft.Extensions.Options.IPostConfigureOptions<Microsoft.Extensions.Http.HttpClientFactoryOptions>, 是因?yàn)?code>OptionsFactory它的構(gòu)造函數(shù)需要的就是這個(gè). 至于有關(guān)Configuration系統(tǒng)的擴(kuò)展和源代碼在這里就不在這里展開了.
使用
至于用它就簡(jiǎn)單了
var factory = ServiceProvider.GetService<IHttpClientFactory>();
var httpClientForBaidu = factory.CreateClient("CallbackProviderSide-baidu");
var httpClientForCnblogs = factory.CreateClient("CallbackProviderSide-Cnblogs");
總結(jié)一下
這樣子, 我們的這個(gè)運(yùn)行時(shí)動(dòng)態(tài)配置HttpClient就算完成了, 我也輕輕松松又水了一篇文章.
另外,有關(guān)IHttpClientFactory背后的故事可以查看文章Exploring the code behind IHttpClientFactory in depth, 很完整的流程圖在配上代碼, 把它講解的清清楚楚.
以上就是asp.net core為IHttpClientFactory添加動(dòng)態(tài)命名配置的詳細(xì)內(nèi)容,更多關(guān)于asp.net core 添加動(dòng)態(tài)命名配置的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
asp.net文件上傳帶進(jìn)度條實(shí)現(xiàn)案例(多種風(fēng)格)
這篇文章主要講解了asp.net文件上傳帶進(jìn)度條實(shí)現(xiàn)案例,有不同風(fēng)格的進(jìn)度條,一定有一款最適合你,感興趣的小伙伴們可以參考一下2015-09-09
ASP.NET MVC @Helper輔助方法和@functons自定義函數(shù)的使用方法
本文主要介紹ASP.NET MVC中使用@Helper和@functons自定義一些代碼片段,方便視圖調(diào)用,從而達(dá)到減少重復(fù)代碼,快速開發(fā)的目的,希望對(duì)大家有所幫助。2016-04-04
.net core實(shí)用技巧——將EF Core生成的SQL語(yǔ)句顯示在控制臺(tái)中
這篇文章主要介紹了如何將EF Core生成的SQL語(yǔ)句顯示在控制臺(tái)中,幫助大家更好的理解和學(xué)習(xí).net core,感興趣的朋友可以了解下2020-08-08
aspx文件格式使用URLRewriter實(shí)現(xiàn)靜態(tài)化變成html
如何隱藏aspx文件格式,變成html,使用asp.net 開發(fā)的網(wǎng)頁(yè)程序,使用URLRewriter.dll 實(shí)現(xiàn)靜態(tài)化,接下來將介紹下具體操作步驟,感興趣的朋友可以參考下2013-04-04
asp.net?core?+?jenkins?實(shí)現(xiàn)自動(dòng)化發(fā)布功能
這篇文章主要介紹了asp.net?core?+?jenkins?實(shí)現(xiàn)自動(dòng)化發(fā)布功能,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
在FireFox/IE下Response中文文件名亂碼問題解決方案
只是針對(duì)沒有空格和IE的情況下使用Response.AppendHeader()如果想在FireFox下輸出沒有編碼的文件,并且IE下輸出的文件名中空格不為+號(hào),就要多一次判斷了,接下來將詳細(xì)介紹下感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-02-02
最鋒利的Visual Studio Web開發(fā)工具擴(kuò)展:Web Essentials使用詳解
Web Essentials是目前為止見過的最好用的VS擴(kuò)展工具了,具體功能請(qǐng)待我一一道來。2016-06-06

