From e2f3245d8f372951dbfc774c52f60290db87d99b Mon Sep 17 00:00:00 2001
From: bicijinlian <bicijinlian@noreply.gitcode.com>
Date: Thu, 8 Aug 2024 20:40:38 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Docs/1.3.0.基础使用.管理客户端.ipynb | 514 +++++++++++++++---
 1 file changed, 429 insertions(+), 85 deletions(-)

diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb
index c2694db..5554b89 100644
--- a/Docs/1.3.0.基础使用.管理客户端.ipynb
+++ b/Docs/1.3.0.基础使用.管理客户端.ipynb
@@ -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容器)"
    ]
   }
  ],