From 8e6c29bb65364b8a103f0e200247d0413f74f08e Mon Sep 17 00:00:00 2001
From: wanggaofeng <15601716045@163.com>
Date: Wed, 24 Jul 2024 18:18:12 +0800
Subject: [PATCH] =?UTF-8?q?doc:=20=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 | 315 +++++++++++++++---
 ... => 1.4.6.高级使用.工厂模式.ipynb} |   0
 2 files changed, 262 insertions(+), 53 deletions(-)
 rename Docs/{1.4.6..高级使用.工厂模式.ipynb => 1.4.6.高级使用.工厂模式.ipynb} (100%)

diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb
index 27397ec..58fca0f 100644
--- a/Docs/1.3.0.基础使用.管理客户端.ipynb
+++ b/Docs/1.3.0.基础使用.管理客户端.ipynb
@@ -25,7 +25,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -37,40 +37,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>System.Net.Http.Json, 8.0.0</span></li></ul></div></div>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "启动WebApi项目\r\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "程序[c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "//全局设置,行运行一次,为后续准备\n",
     "#r \"nuget:System.Net.Http.Json\"\n",
@@ -117,17 +84,17 @@
     "\n",
     "因为HttpClient刚推出时不成熟及微软官方文档的示例代码是这种用法,再加上这种是最简单方便的使用方法,就造成很多人使用这种用法。\n",
     "这种方法有如下缺点:\n",
-    "+ 1. 每次使用都实例化,造成性能开销大、容易内存泄露;\n",
-    "+ 2. 并发量大、请求频繁时:网络端口会被耗尽 `Using包HttpClient,也只是在应用进程中释放了HttpClient实例,但http请求/响应是跨操作系统和网络的,而系统及网络问题在进程之上,不是进程所能处理的。`\n",
+    "  1. 每次使用都实例化,造成性能开销大、容易内存泄露;\n",
+    "  2. 并发量大、请求频繁时:网络端口会被耗尽 `Using包HttpClient,也只是在应用进程中释放了HttpClient实例,但http请求/响应是跨操作系统和网络的,而系统及网络问题在进程之上,不是进程所能处理的。`\n",
     "  \n",
     "优点:\n",
-    "+ 1. 使用简单,好学易用;\n",
-    "+ 2. 并发量小且请求不频繁时,问题不大;"
+    "  1. 使用简单,好学易用;\n",
+    "  2. 并发量小且请求不频繁时,问题不大;"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -139,26 +106,18 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "第 10 次/ 共 10 次请求,成功!"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
+   "outputs": [],
    "source": [
     "/*\n",
     "    每次请求都实例化:并发量大、请求频繁进会耗尽套接字端口\n",
     "*/\n",
     "{ \n",
+    "    var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
+    "\n",
     "    using(var client = new HttpClient())\n",
     "    {\n",
     "        //发送请求\n",
-    "        var response = await client.GetAsync(\"http://localhost\");\n",
+    "        var response = await client.GetAsync(baseUrl);\n",
     "        response.EnsureSuccessStatusCode();\n",
     "    }\n",
     "\n",
@@ -169,7 +128,7 @@
     "    {\n",
     "        using(var client = new HttpClient())\n",
     "        {\n",
-    "            var response = await client.GetAsync(\"http://localhost\");\n",
+    "            var response = await client.GetAsync(baseUrl);\n",
     "            response.EnsureSuccessStatusCode();\n",
     "            displayValue.Update($\"第 {i+1} 次/ 共 10 次请求,成功!\");\n",
     "        }\n",
@@ -184,6 +143,239 @@
     "## 2、手动管理:静态类或单例"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "相比于直接new,实现了HttpClient的重用,`不推荐的`:\n",
+    "\n",
+    "缺点:\n",
+    "  1. 不够灵活、优雅:特别是有多个系列的请求时;\n",
+    "  \n",
+    "优点:\n",
+    "  1. 复用 HttpClient\n",
+    "  2. 实现了HttpClient的重用,减少创建和销毁的开销"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "/*\n",
+    "    静态类/属性\n",
+    "*/\n",
+    "\n",
+    "public class HttpClientHelper\n",
+    "{\n",
+    "    public readonly static HttpClient StaticClient;\n",
+    "\n",
+    "    static HttpClientHelper()\n",
+    "    {\n",
+    "        SocketsHttpHandler handler = new SocketsHttpHandler()\n",
+    "        {\n",
+    "            PooledConnectionLifetime = TimeSpan.FromSeconds(30),\n",
+    "        };\n",
+    "\n",
+    "        StaticClient = new HttpClient(handler);\n",
+    "\n",
+    "        //统一设置:请求头等\n",
+    "\n",
+    "        //统一错误处理\n",
+    "\n",
+    "        //当然这里也可以设置Pipline,不过这里就不演示了\n",
+    "    } \n",
+    "\n",
+    "    public static async Task<HttpResponseMessage> GetAsync(string url)\n",
+    "    {\n",
+    "        return await StaticClient.GetAsync(url);\n",
+    "    }\n",
+    "\n",
+    "    public static async Task<string> GetStringAsync(string url)\n",
+    "    {\n",
+    "        var response = await StaticClient.GetAsync(url);\n",
+    "        response.EnsureSuccessStatusCode();\n",
+    "        return await response.Content.ReadAsStringAsync();\n",
+    "    }\n",
+    "\n",
+    "    public static async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)\n",
+    "    {\n",
+    "        return await StaticClient.PostAsync(url, content);\n",
+    "    }\n",
+    "}\n",
+    "\n",
+    "{   //调用静态类\n",
+    "    var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
+    "    var response = await HttpClientHelper.GetAsync(baseUrl+\"/api/Config/GetApiConfig\");\n",
+    "    var content = await response.Content.ReadAsStringAsync();\n",
+    "    Console.WriteLine(content);\n",
+    "\n",
+    "    var response2 = await HttpClientHelper.GetStringAsync(baseUrl+\"/api/Normal/GetAllAccounts\");\n",
+    "    Console.WriteLine(response2);\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "/*\n",
+    "   单例实现1:\n",
+    "   1. 私有构造函数,防止外部实例化\n",
+    "   2. 使用静态只读变量存储类的实例,由.Net框架保证实例不变且线程安全\n",
+    "   3. 密封类,拒绝继承,保证不被子类破坏\n",
+    "*/\n",
+    "\n",
+    "// 使用Lazy<T>实现单例\n",
+    "public sealed class HttpClientSingleton \n",
+    "{\n",
+    "    // 私有静态变量,用于存储类的实例\n",
+    "    private  static  readonly  HttpClientSingleton  instance  =  new  HttpClientSingleton();\n",
+    "    \n",
+    "    //公共静态属性,用于获取类的实例\n",
+    "    public  static  HttpClientSingleton  Instance\n",
+    "    {\n",
+    "        get\n",
+    "        {\n",
+    "            return  instance;\n",
+    "        }\n",
+    "    }\n",
+    "\n",
+    "    private  readonly  HttpClient  Client;\n",
+    "\n",
+    "    //私有构造函数,防止外部实例化\n",
+    "    private HttpClientSingleton() \n",
+    "    {\n",
+    "        SocketsHttpHandler handler = new SocketsHttpHandler()\n",
+    "        {\n",
+    "            PooledConnectionLifetime = TimeSpan.FromSeconds(30),\n",
+    "        };\n",
+    "\n",
+    "        Client = new HttpClient(handler);\n",
+    "\n",
+    "        //统一设置:请求头等\n",
+    "\n",
+    "        //统一错误处理\n",
+    "\n",
+    "        //可以使用IoC容器来管理\n",
+    "\n",
+    "        //当然这里也可以设置Pipline,不过这里就不演示了\n",
+    "        Console.WriteLine(\"HttpClientSingleton 初始化一次\");\n",
+    "    }\n",
+    "\n",
+    "    public async Task<HttpResponseMessage> GetAsync(string url)\n",
+    "    {\n",
+    "        return await Client.GetAsync(url);\n",
+    "    }\n",
+    "\n",
+    "    public async Task<string> GetStringAsync(string url)\n",
+    "    {\n",
+    "        var response = await Client.GetAsync(url);\n",
+    "        response.EnsureSuccessStatusCode();\n",
+    "        return await response.Content.ReadAsStringAsync();\n",
+    "    }\n",
+    "}\n",
+    "\n",
+    "{ //调用示例\n",
+    "\n",
+    "    var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
+    "    var response = await HttpClientSingleton.Instance.GetAsync(baseUrl+\"/api/Config/GetApiConfig\");\n",
+    "    var content = await response.Content.ReadAsStringAsync();\n",
+    "    Console.WriteLine(content);\n",
+    "\n",
+    "    var response2 = await HttpClientSingleton.Instance.GetStringAsync(baseUrl+\"/api/Normal/GetAllAccounts\");\n",
+    "    Console.WriteLine(response2);\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "/*\n",
+    "   单例实现2:\n",
+    "   1. 私有构造函数,防止外部实例化\n",
+    "   2. 使用Lazy<T>, 延迟实例化, 由.Net 框架保证线程安全\n",
+    "   3. 密封类,拒绝继承,保证不被子类破坏\n",
+    "*/\n",
+    "\n",
+    "// 由于静态初始化器是由 .NET  运行时在后台处理的,因此它是线程安全的,不需要额外的锁定机制。\n",
+    "public sealed class HttpClientSingleton2\n",
+    "{\n",
+    "    private  static  readonly  Lazy<HttpClient>  _httpClientLazy  =  new  Lazy<HttpClient>(()  =>\n",
+    "    {\n",
+    "         SocketsHttpHandler handler = new SocketsHttpHandler()\n",
+    "        {\n",
+    "            PooledConnectionLifetime = TimeSpan.FromSeconds(30)\n",
+    "        };\n",
+    "\n",
+    "        var  client  =  new  HttpClient(handler)\n",
+    "        {\n",
+    "            //  可以在这里配置HttpClient的实例,例如设置超时时间、基地址等\n",
+    "            //Timeout  =  TimeSpan.FromSeconds(30),\n",
+    "            //BaseAddress  =  new  Uri(\"https://api.example.com/\"),\n",
+    "        };\n",
+    "\n",
+    "\n",
+    "        //统一设置:请求头等\n",
+    "\n",
+    "        //统一错误处理\n",
+    "\n",
+    "        //可以使用IoC容器来管理\n",
+    "\n",
+    "        //当然这里也可以设置Pipline,不过这里就不演示了\n",
+    "        Console.WriteLine(\"HttpClientSingleton2 初始化一次\");\n",
+    "\n",
+    "        return  client;\n",
+    "    });\n",
+    "\n",
+    "    public  static  HttpClient  Instance  =>  _httpClientLazy.Value;\n",
+    "\n",
+    "    //  私有构造函数,防止外部实例化\n",
+    "    private  HttpClientSingleton2()  {  }  \n",
+    "\n",
+    "    public async Task<HttpResponseMessage> GetAsync(string url)\n",
+    "    {\n",
+    "        return await _httpClientLazy.Value.GetAsync(url);\n",
+    "    }\n",
+    "\n",
+    "    public async Task<string> GetStringAsync(string url)\n",
+    "    {\n",
+    "        var response = await _httpClientLazy.Value.GetAsync(url);\n",
+    "        response.EnsureSuccessStatusCode();\n",
+    "        return await response.Content.ReadAsStringAsync();\n",
+    "    }\n",
+    "}\n",
+    "\n",
+    "{ //调用示例\n",
+    "\n",
+    "    var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
+    "    var response = await HttpClientSingleton2.Instance.GetAsync(baseUrl+\"/api/Config/GetApiConfig\");\n",
+    "    var content = await response.Content.ReadAsStringAsync();\n",
+    "    Console.WriteLine(content);\n",
+    "\n",
+    "    var response2 = await HttpClientSingleton2.Instance.GetStringAsync(baseUrl+\"/api/Normal/GetAllAccounts\");\n",
+    "    Console.WriteLine(response2);\n",
+    "}"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -191,6 +383,23 @@
     "## 3、手动管理:多工具类(每类请求对应一种工具类或单例类)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "把不同类别的请求分成不同的工具类,业务类直接封装成工具类的方法。类似类型化的客户端。 简单使用的话,`比较推荐`\n",
+    "\n",
+    "优点:\n",
+    "  1. 复用HttpClient\n",
+    "  2. 可以灵活的进行统一配置\n",
+    "  3. 不同类别不同工具类,方便定制\n",
+    "  4. 业务直接封装成工具类方法,调用方便、快捷\n",
+    "\n",
+    "缺点:\n",
+    "  1. 工具类比较多,需要手动维护\n",
+    "  2. 工具类方法比较多且和业务直接相关,需要手动维护\n"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
diff --git a/Docs/1.4.6..高级使用.工厂模式.ipynb b/Docs/1.4.6.高级使用.工厂模式.ipynb
similarity index 100%
rename from Docs/1.4.6..高级使用.工厂模式.ipynb
rename to Docs/1.4.6.高级使用.工厂模式.ipynb