|
|
|
@ -32,7 +32,7 @@
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": 5,
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
"metadata": {
|
|
|
|
|
"dotnet_interactive": {
|
|
|
|
|
"language": "csharp"
|
|
|
|
@ -44,26 +44,7 @@
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"data": {
|
|
|
|
|
"text/html": [
|
|
|
|
|
"<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.DependencyInjection, 8.0.0</span></li><li><span>Microsoft.Extensions.Http, 8.0.0</span></li><li><span>Microsoft.Extensions.Http.Polly, 8.0.7</span></li><li><span>Polly, 8.4.1</span></li><li><span>Refit, 7.1.2</span></li><li><span>Refit.HttpClientFactory, 7.1.2</span></li><li><span>System.Net.Http.Json, 8.0.0</span></li></ul></div></div>"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"output_type": "display_data"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "stdout",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
|
|
|
|
"配置文件根目录:e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
|
|
|
|
|
"启动WebApi项目\n",
|
|
|
|
|
"程序[e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\n"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": [
|
|
|
|
|
"//全局设置,行运行一次,为后续准备\n",
|
|
|
|
|
"#r \"nuget:System.Net.Http.Json\"\n",
|
|
|
|
@ -855,6 +836,159 @@
|
|
|
|
|
"IHttpClientFactory 综合使用了 HttpClient的多种特性:HttpClient的生命周期、HttpClient的配置、HttpClient的拦截器、HttpClient的缓存、HttpClient的依赖注入、Polly等等。\n"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"### 默认客户端"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"从使用推测,设计 IHttpClientFactory 时,重点应该是使用 “命名客户端” 或 “类型化客户端” 而不是默认客户端。 \n",
|
|
|
|
|
"\n",
|
|
|
|
|
"只有 AddHttpClient() 扩展方法返回 IServiceCollection;其它相关扩展方法( AddHttpClient<HttpClient>())均返回 IHttpClientBuilder,明显针对命名客户端。\n",
|
|
|
|
|
"AddHttpClient() 相当于注册了基本框架;而命名客户端中,名称为空(\"\"或string.Empty)的,相当于默认客户端。\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"有一个 名为 `ConfigureHttpClientDefaults` 的 ServiceCollection 对象的扩展方法,用于配置所有HttpClient实例,并且只在初始化时执行一次。如果只使用一个默认客户端的话,可以使用 ConfigureHttpClientDefaults 和 AddHttpClient() 配合使用,也能达到默认客户端的配置效果。 "
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
"metadata": {
|
|
|
|
|
"vscode": {
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": [
|
|
|
|
|
"//方式1:默认客户端\n",
|
|
|
|
|
"{ \n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" /*\n",
|
|
|
|
|
" AddHttpClient() 返回 ServiceCollection,可以继续添加其他客户端。\n",
|
|
|
|
|
" 其它方法则返回IHttpClientBuilder,后结配置的扩展方法,只能针对当前前端那个命名命令端。\n",
|
|
|
|
|
" */\n",
|
|
|
|
|
" services.AddHttpClient();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var client = factory.CreateClient();\n",
|
|
|
|
|
" //或者\n",
|
|
|
|
|
" var client2 = factory.CreateClient(\"\");\n",
|
|
|
|
|
" //或者 内部都是使用CreateClient(string.Empty),表示默认客户端。\n",
|
|
|
|
|
" var client3 = factory.CreateClient(string.Empty);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var response = await client.GetAsync(webApiBaseUrl + \"/api/hello/index\");\n",
|
|
|
|
|
" response.EnsureSuccessStatusCode();\n",
|
|
|
|
|
" var data = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" data.Display();\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"//方式2:默认客户端 + 默认配置\n",
|
|
|
|
|
"{ \n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //默认客户端\n",
|
|
|
|
|
" services.AddHttpClient();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //配置所有客户端\n",
|
|
|
|
|
" services.ConfigureHttpClientDefaults(builder => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" //配置构建器\n",
|
|
|
|
|
" //builder.AddDefaultLogger();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //配置客户端\n",
|
|
|
|
|
" builder.ConfigureHttpClient(c=>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" c.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
" var client = factory.CreateClient();\n",
|
|
|
|
|
" var response = await client.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" response.EnsureSuccessStatusCode();\n",
|
|
|
|
|
" var data = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" data.Display();\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"//方式3(推荐):默认客户端:直接使用名称为 string.empty 的命名客户端\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //默认客户端\n",
|
|
|
|
|
" services\n",
|
|
|
|
|
" .AddHttpClient<HttpClient>(string.Empty)\n",
|
|
|
|
|
" //这样后续的配置,都是针对 string.empty 的客户端,可以使用全部配置功能\n",
|
|
|
|
|
" .ConfigureHttpClient(c=>c.BaseAddress = new Uri(webApiBaseUrl))\n",
|
|
|
|
|
" .AddDefaultLogger();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
" var client = factory.CreateClient();\n",
|
|
|
|
|
" var response = await client.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" response.EnsureSuccessStatusCode();\n",
|
|
|
|
|
" var data = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" data.Display();\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"//错误用法\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //默认客户端\n",
|
|
|
|
|
" services\n",
|
|
|
|
|
" //没有参数时,导致后面配置不起使用;\n",
|
|
|
|
|
" //参数必须为 空字符串或string.Empty,后续的配置才能起使用\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" .AddHttpClient<HttpClient>()\n",
|
|
|
|
|
" //没有参数时,导致后面配置不起使用\n",
|
|
|
|
|
" .ConfigureHttpClient(c=>c.BaseAddress = new Uri(webApiBaseUrl))\n",
|
|
|
|
|
" .AddDefaultLogger();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
" var client = factory.CreateClient();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" try\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" var response = await client.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" response.EnsureSuccessStatusCode();\n",
|
|
|
|
|
" var data = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" data.Display();\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
" catch(InvalidOperationException ex)\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" Console.WriteLine($\"没有参数的配置:AddHttpClient<HttpClient>(),因后续配置中,赋值 BaseAddress 不起使用,出现异常:{Environment.NewLine}{ex.Message}\");\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
" catch(Exception ex)\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" Console.WriteLine(ex.Message);\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
" finally\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.Dispose();\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
"}"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"### 默认全局配置"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"ConfigureHttpClientDefaults 扩展方法,添加一个委托,用于配置所有HttpClient实例。只执行一次。"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": null,
|
|
|
|
@ -871,10 +1005,11 @@
|
|
|
|
|
},
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": [
|
|
|
|
|
"//所有HttpClient配置\n",
|
|
|
|
|
"//全局配置:所有HttpClient配置\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" //添加一个委托,用于配置所有HttpClient实例。只执行一次。\n",
|
|
|
|
|
" //添加一个委托,用于配置所有HttpClient实例。\n",
|
|
|
|
|
" //只执行一次,而非每次CreateClient,都会执行一次。\n",
|
|
|
|
|
" services.ConfigureHttpClientDefaults(builder => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" //builder.UseSocketsHttpHandler();\n",
|
|
|
|
@ -884,60 +1019,83 @@
|
|
|
|
|
" {\n",
|
|
|
|
|
" hc.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var client = factory.CreateClient();\n",
|
|
|
|
|
" var response = await client.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" var data = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(data);\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
" Console.WriteLine(\"ConfigureHttpClientDefaults 只执行一次!\");\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"/*\n",
|
|
|
|
|
" 默认 HttpClient\n",
|
|
|
|
|
" 1、配置默认HttpClient时,AddHttpClient(Action)的Action不会被执行,ConfigureHttpClient()也不执行。原因暂时不明\n",
|
|
|
|
|
" 2、想配置默认HttpClient,可以使用ConfigureHttpClientDefaults方法,但此方法针对所有HttpClient实例(包括命名HttpClient)\n",
|
|
|
|
|
"*/\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" //配置命名客户端\n",
|
|
|
|
|
" services\n",
|
|
|
|
|
" .AddHttpClient<HttpClient>(\"client_a\")\n",
|
|
|
|
|
" .ConfigureHttpClient(hc => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" hc.DefaultRequestHeaders.Add(\"client_a\", \"client_a\");\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //默认HttpClient\n",
|
|
|
|
|
" services.AddHttpClient<HttpClient>();\n",
|
|
|
|
|
" //可以覆盖默认配置\n",
|
|
|
|
|
" //hc.BaseAddress = new Uri(\"http://www.qq.com\");\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //这种形式,不生效\n",
|
|
|
|
|
" // services.AddHttpClient<HttpClient>((provider, client) => \n",
|
|
|
|
|
" // {\n",
|
|
|
|
|
" // client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" // });\n",
|
|
|
|
|
" Console.WriteLine(\"ConfigureHttpClient 每次 CreateClient 执行一次!\");\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //这种形式,ConfigureHttpClient 也不生效\n",
|
|
|
|
|
" // services.AddHttpClient<HttpClient>()\n",
|
|
|
|
|
" // .ConfigureHttpClient(config=>\n",
|
|
|
|
|
" // {\n",
|
|
|
|
|
" // config.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" // });\n",
|
|
|
|
|
" \n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var provider = services.BuildServiceProvider();\n",
|
|
|
|
|
" //默认客户端\n",
|
|
|
|
|
" var defaultClient = factory.CreateClient();\n",
|
|
|
|
|
" var defaultResponse = await defaultClient.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" var defaultData = await defaultResponse.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(defaultData);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = provider.GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
" //命名客户端\n",
|
|
|
|
|
" var namedClient = factory.CreateClient(\"client_a\");\n",
|
|
|
|
|
" var namedResponse = await namedClient.GetAsync(\"/api/hello/get\");\n",
|
|
|
|
|
" var namedData = await namedResponse.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(namedData);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var response = await factory.CreateClient().GetAsync(webApiBaseUrl+\"/api/hello/index\");\n",
|
|
|
|
|
" var content = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" \n",
|
|
|
|
|
" Console.WriteLine(content);\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"//命名 HttpClient\n",
|
|
|
|
|
" _ = factory.CreateClient(\"client_a\");\n",
|
|
|
|
|
"}"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"### 命名客户端(推荐用法)"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"命名客户端,应该是官方推荐的方法。名称为空字符串或string.Empty时,可以为是默认命名客户端,factory.CreateClient()创建的就是这个默认客户端(或者factory.CreateClient(\"\"))。"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
"metadata": {
|
|
|
|
|
"vscode": {
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": [
|
|
|
|
|
"//命名客户端\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" var clientA =\"httpClientA\";\n",
|
|
|
|
|
" var clientB =\"httpClientB\";\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" services.AddHttpClient<HttpClient>(string.Empty, (provider, client) => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" services.AddHttpClient<HttpClient>(clientA, (provider, client) => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" services.AddHttpClient<HttpClient>(clientB, (provider, client) => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;\n",
|
|
|
|
@ -951,15 +1109,57 @@
|
|
|
|
|
"\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var responseA = await factory.CreateClient(clientA).GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" var contentA = await responseA.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(contentA);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var responseB = await factory.CreateClient(clientB).PostAsync(\"/api/hello/post\",null);\n",
|
|
|
|
|
" var contentB = await responseB.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(contentB);\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //name=string.Empty\n",
|
|
|
|
|
" var defaultClient = factory.CreateClient();\n",
|
|
|
|
|
" var defaultResponse = await defaultClient.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" var defaultData = await defaultResponse.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(defaultData);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //name=clientA\n",
|
|
|
|
|
" var httpClient_a = factory.CreateClient(clientA);\n",
|
|
|
|
|
" var responseA = await httpClient_a.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" var dataA = await responseA.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" dataA.Display();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //name=clientB\n",
|
|
|
|
|
" var httpClient_B = factory.CreateClient(clientB);\n",
|
|
|
|
|
" var responseB = await httpClient_B.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" var dataB = await responseB.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" dataB.Display();\n",
|
|
|
|
|
"}"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"### 类型化客户端 (推荐)"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {
|
|
|
|
|
"vscode": {
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"source": [
|
|
|
|
|
"类型化的客户端,两种基本使用方式:\n",
|
|
|
|
|
"1、可以单独使用(直接IoC容器)\n",
|
|
|
|
|
"2、与IFactoryHttpClient配合使用(依赖注入),目的是:从统一的工厂配置中获取客户端,作为 HttpClient 类型的实参,传给类型化客户端的构造函数。\n",
|
|
|
|
|
" 换名话说:从工厂获取HttpClient实例,设置为 类型化客户端类的 HttpClient,在其内部使用。"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
"metadata": {
|
|
|
|
|
"vscode": {
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": [
|
|
|
|
|
"// 类型化客户端 HttpClient\n",
|
|
|
|
|
"public class HttpClientServiceA\n",
|
|
|
|
|
"{\n",
|
|
|
|
@ -995,13 +1195,73 @@
|
|
|
|
|
" }\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"// 方式1(不推荐):类型化客户端:直接注入IoC,并从中获取实例。优点是范围可以自己选择。\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" Console.WriteLine(\"方式1 -------------------------------------------------------------------\");\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" services.AddSingleton<HttpClientServiceA>(b => \n",
|
|
|
|
|
" { \n",
|
|
|
|
|
" return new HttpClientServiceA(new HttpClient(){BaseAddress = new Uri(webApiBaseUrl)});\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
" services.AddScoped<HttpClientServiceB>(b=> \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" return new HttpClientServiceB(new HttpClient(){BaseAddress = new Uri(webApiBaseUrl)});\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var builder = services.BuildServiceProvider();\n",
|
|
|
|
|
" var serverA = builder.GetRequiredService<HttpClientServiceA>();\n",
|
|
|
|
|
" var serverB = builder.GetRequiredService<HttpClientServiceB>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var dataA = await serverA.GetIndexAsync();\n",
|
|
|
|
|
" Console.WriteLine(dataA);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var dataB = await serverB.PingAsync();\n",
|
|
|
|
|
" Console.WriteLine(dataB);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" Console.WriteLine(\"========================================================================\");\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"// 方式2:类型化客户端:AddHttpClient<>() 设置\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" Console.WriteLine(\"方式2 -------------------------------------------------------------------\");\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" services\n",
|
|
|
|
|
" .AddHttpClient<HttpClientServiceA>()\n",
|
|
|
|
|
" .ConfigureHttpClient(client=>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" services\n",
|
|
|
|
|
" .AddHttpClient<HttpClientServiceB>()\n",
|
|
|
|
|
" .ConfigureHttpClient(client=>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var builder = services.BuildServiceProvider();\n",
|
|
|
|
|
" var serverA = builder.GetRequiredService<HttpClientServiceA>();\n",
|
|
|
|
|
" var serverB = builder.GetRequiredService<HttpClientServiceB>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var dataA = await serverA.GetIndexAsync();\n",
|
|
|
|
|
" Console.WriteLine(dataA);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var dataB = await serverB.PingAsync();\n",
|
|
|
|
|
" Console.WriteLine(dataB);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" Console.WriteLine(\"========================================================================\");\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"// 方式3:类型化客户端:结合工厂,由工厂从统一配置中提供类型化客户端中使用的HttpClient实例。\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" Console.WriteLine(\"方式3 -------------------------------------------------------------------\");\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" services.AddHttpClient<HttpClientServiceA>(client => \n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" Console.WriteLine(\"HttpClientServiceA => AddHttpClient 执行一次\");\n",
|
|
|
|
|
" })\n",
|
|
|
|
|
" .AddTypedClient<HttpClientServiceA>()\n",
|
|
|
|
|
" .ConfigureHttpClient(client=>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.Timeout = TimeSpan.FromSeconds(1);\n",
|
|
|
|
@ -1013,6 +1273,7 @@
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" Console.WriteLine(\"HttpClientServiceB => AddHttpClient 执行一次\");\n",
|
|
|
|
|
" })\n",
|
|
|
|
|
" .AddTypedClient<HttpClientServiceB>()\n",
|
|
|
|
|
" .ConfigureHttpClient(client=>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.Timeout = TimeSpan.FromSeconds(2);\n",
|
|
|
|
@ -1034,39 +1295,122 @@
|
|
|
|
|
"\n",
|
|
|
|
|
" var dataB2 = await serviceB2.PingAsync();\n",
|
|
|
|
|
" Console.WriteLine(dataB2);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" Console.WriteLine(\"========================================================================\");\n",
|
|
|
|
|
"}"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"### 管道配置"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": 150,
|
|
|
|
|
"metadata": {
|
|
|
|
|
"vscode": {
|
|
|
|
|
"languageId": "polyglot-notebook"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"name": "stdout",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
|
|
|
|
"LoggerDelegatingHandler -> SendAsync -> Before\n",
|
|
|
|
|
"LoggerDelegatingHandler -> SendAsync -> After\n",
|
|
|
|
|
"Pong\n"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"source": [
|
|
|
|
|
"//管道配置\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"//日志中间件(管道类)\n",
|
|
|
|
|
"public class LoggerDelegatingHandler : DelegatingHandler\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" Console.WriteLine(\"LoggerDelegatingHandler -> Send -> Before\");\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" HttpResponseMessage response = base.Send(request, cancellationToken);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" Console.WriteLine(\"LoggerDelegatingHandler -> Send -> After\");\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" return response;\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" Console.WriteLine(\"LoggerDelegatingHandler -> SendAsync -> Before\");\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" HttpResponseMessage response = await base.SendAsync(request, cancellationToken);\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" Console.WriteLine(\"LoggerDelegatingHandler -> SendAsync -> After\");\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" return response;\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"// 类型化客户端2\n",
|
|
|
|
|
"//使用日志中间件\n",
|
|
|
|
|
"{\n",
|
|
|
|
|
" var services = new ServiceCollection();\n",
|
|
|
|
|
" services.AddHttpClient<HttpClientServiceA>(client => \n",
|
|
|
|
|
"\n",
|
|
|
|
|
" //先注册\n",
|
|
|
|
|
" services.AddTransient<LoggerDelegatingHandler>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" services.AddHttpClient<HttpClient>(string.Empty).ConfigureHttpClient(client =>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
|
|
|
|
|
" Console.WriteLine(\"HttpClientServiceA => AddHttpClient 执行一次\");\n",
|
|
|
|
|
" })\n",
|
|
|
|
|
" .AddTypedClient<HttpClientServiceA>()\n",
|
|
|
|
|
" .ConfigureHttpClient(client=>\n",
|
|
|
|
|
" //配置SocketsHttpHandler\n",
|
|
|
|
|
" .UseSocketsHttpHandler((handler,provider) =>\n",
|
|
|
|
|
" {\n",
|
|
|
|
|
" client.Timeout = TimeSpan.FromSeconds(1);\n",
|
|
|
|
|
" Console.WriteLine(\"HttpClientServiceA => ConfigureHttpClient 执行一次\");\n",
|
|
|
|
|
" });\n",
|
|
|
|
|
"}\n",
|
|
|
|
|
" handler.ConnectTimeout = TimeSpan.FromSeconds(10);\n",
|
|
|
|
|
" handler.MaxConnectionsPerServer = 100;\n",
|
|
|
|
|
" handler.UseProxy = false;\n",
|
|
|
|
|
" handler.UseCookies = true;\n",
|
|
|
|
|
" handler.EnableMultipleHttp2Connections = true;\n",
|
|
|
|
|
" handler.SslOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;\n",
|
|
|
|
|
" })\n",
|
|
|
|
|
" //使用前先在AddTransient范围注册\n",
|
|
|
|
|
" //.AddHttpMessageHandler<LoggerDelegatingHandler>()\n",
|
|
|
|
|
" .AddHttpMessageHandler<LoggerDelegatingHandler>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"//todo: 生成式 HttpClient: Refit库\n",
|
|
|
|
|
" var factory = services.BuildServiceProvider().GetService<IHttpClientFactory>();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"/*\n",
|
|
|
|
|
" IFactoryHttpClient + Polly8\n",
|
|
|
|
|
" 1、引用库 Polly 和 Microsoft.Extensions.Http.Polly\n",
|
|
|
|
|
"*/\n",
|
|
|
|
|
" var client = factory.CreateClient();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" var response = await client.GetAsync(\"/api/hello/ping\");\n",
|
|
|
|
|
" response.EnsureSuccessStatusCode();\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"\n"
|
|
|
|
|
" var responseString = await response.Content.ReadAsStringAsync();\n",
|
|
|
|
|
" Console.WriteLine(responseString);\n",
|
|
|
|
|
"}"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"### 日志配置"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"## 7 工厂 + Polly V8"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
|
|
|
|
"## 7、综合管理:工厂 + 类型化客户端 + 请求管道 + Polly(默认使用 连接池和IoC容器)"
|
|
|
|
|
"## 8、综合管理:工厂 + 类型化客户端 + 请求管道 + Polly(默认使用 连接池和IoC容器)"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|