From bb01db099f175a3da64224fc870b40eceea62a6f Mon Sep 17 00:00:00 2001
From: bicijinlian <bicijinlian@163.com>
Date: Sat, 29 Jun 2024 21:11:53 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=87=86=E5=88=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Docs/1.0项目管理.dib                      |  77 +++---
 Docs/1.2.使用准则.ipynb                   | 237 ++++++++----------
 Docs/1.6.测试.ipynb                         | 111 ++++++--
 .../ConfigFiles/Config.json                   |   5 +-
 HttpClientStudy.Config/WebApiConfig.cs        |  31 ++-
 .../ConfigTest/WebApiConfigTest.cs            |   9 +
 .../HttpClients/BaseHttpClientTest.cs         |  19 +-
 HttpClientStudy.WebApp/appsettings.json       |   2 +-
 8 files changed, 295 insertions(+), 196 deletions(-)

diff --git a/Docs/1.0项目管理.dib b/Docs/1.0项目管理.dib
index 03ccc24..c366b4e 100644
--- a/Docs/1.0项目管理.dib
+++ b/Docs/1.0项目管理.dib
@@ -21,7 +21,7 @@ dotnet build ..\HttpClientStudy.sln
 
 #!pwsh
 
-# 可以发布整个项目,但要发布到默认目录下
+# 可以发布整个项目,但要发布到默认目录下,指定输出目录的话,会把所有项目都发布到一个目录,造成混乱。
 # dotnet publish ..\HttpClientStudy.sln
 
 # 分项目发布到Docs目录下
@@ -78,52 +78,55 @@ using System.Diagnostics;
 using System.Reflection;
 using Microsoft.Extensions.DependencyInjection;
 
- var pathDic = new Dictionary<string, (string desc, string? path)>() 
- {
-     //当前运行的exe的完整路径,包含exe文件名,只用于WinForm
-     {"Application.ExecutablePath",("程序集基完整路径(仅WinForm)", "Application.ExecutablePath 只适用于WinForm") },
+//查看各种程序路径
+{
+    var pathDic = new Dictionary<string, (string desc, string? path)>() 
+    {
+        //当前运行的exe的完整路径,包含exe文件名,只用于WinForm
+        {"Application.ExecutablePath",("程序集基完整路径(仅WinForm)", "Application.ExecutablePath 只适用于WinForm") },
 
-     //程序的启动路径:只用于WinForm
-     {"Application.StartupPath",("程序集启动路径(仅WinForm)", "Application.StartupPath 只适用于WinForm") },
+        //程序的启动路径:只用于WinForm
+        {"Application.StartupPath",("程序集启动路径(仅WinForm)", "Application.StartupPath 只适用于WinForm") },
 
-     //当前执行的exe或启动项目的路径,通过AppContext
-     {"AppContext.BaseDirectory",("执行或启动路径", AppContext.BaseDirectory) },  
+        //当前执行的exe或启动项目的路径,通过AppContext
+        {"AppContext.BaseDirectory",("执行或启动路径", AppContext.BaseDirectory) },  
 
-     //当前执行的exe的目录,不包含exe名,使用AppDomain
-     {"AppDomain.CurrentDomain.BaseDirectory",("程序集解析程序用于探测程序集的基目录", AppDomain.CurrentDomain.BaseDirectory) }, 
+        //当前执行的exe的目录,不包含exe名,使用AppDomain
+        {"AppDomain.CurrentDomain.BaseDirectory",("程序集解析程序用于探测程序集的基目录", AppDomain.CurrentDomain.BaseDirectory) }, 
 
-     //程序安装或启动基目录  包含应用程序的目录的名称
-     {"AppDomain.CurrentDomain.SetupInformation.ApplicationBase",("程序安装或启动基目录", AppDomain.CurrentDomain.SetupInformation.ApplicationBase) }, 
+        //程序安装或启动基目录  包含应用程序的目录的名称
+        {"AppDomain.CurrentDomain.SetupInformation.ApplicationBase",("程序安装或启动基目录", AppDomain.CurrentDomain.SetupInformation.ApplicationBase) }, 
 
-     //当前进程的主模块路径,包含exe名
-     {"Process.GetCurrentProcess().MainModule.FileName",("当前进程的主模块路径", Process.GetCurrentProcess()?.MainModule?.FileName) }, 
+        //当前进程的主模块路径,包含exe名
+        {"Process.GetCurrentProcess().MainModule.FileName",("当前进程的主模块路径", Process.GetCurrentProcess()?.MainModule?.FileName) }, 
 
-     //环境变量:用户当前工作目录的完整限定路径
-     {"Environment.CurrentDirectory",("用户当前工作目录的完整限定路径", Environment.CurrentDirectory) }, 
+        //环境变量:用户当前工作目录的完整限定路径
+        {"Environment.CurrentDirectory",("用户当前工作目录的完整限定路径", Environment.CurrentDirectory) }, 
 
-     //环境变量:当前exe的完整路径,包含exe名,通过命令行参数
-     {"Environment.GetCommandLineArgs()[0]",("当前exe的完整路径", Environment.GetCommandLineArgs()[0]) }, 
+        //环境变量:当前exe的完整路径,包含exe名,通过命令行参数
+        {"Environment.GetCommandLineArgs()[0]",("当前exe的完整路径", Environment.GetCommandLineArgs()[0]) }, 
 
-     //当前工作目录的路径(可变)
-     {"Directory.GetCurrentDirectory",("当前工作目录的路径(可变)", Directory.GetCurrentDirectory()) },
-     
-     //当前Assembly的加载路径,包含dll或exe名
-     {"Assembly.GetExecutingAssembly().Location",("当前Assembly的加载路径", Assembly.GetExecutingAssembly().Location) },
+        //当前工作目录的路径(可变)
+        {"Directory.GetCurrentDirectory",("当前工作目录的路径(可变)", Directory.GetCurrentDirectory()) },
+        
+        //当前Assembly的加载路径,包含dll或exe名
+        {"Assembly.GetExecutingAssembly().Location",("当前Assembly的加载路径", Assembly.GetExecutingAssembly().Location) },
 
-     //入口程序集的路径
-     {"Assembly.GetEntryAssembly().Location",("入口程序集的路径", Assembly.GetEntryAssembly()?.Location) },
+        //入口程序集的路径
+        {"Assembly.GetEntryAssembly().Location",("入口程序集的路径", Assembly.GetEntryAssembly()?.Location) },
 
-     //已过时:当前程序集的CodeBase路径,可能为file URI格式
-     {"Assembly.GetExecutingAssembly().CodeBase",("当前程序集的CodeBase路径", Assembly.GetExecutingAssembly()?.CodeBase) },
+        //已过时:当前程序集的CodeBase路径,可能为file URI格式
+        {"Assembly.GetExecutingAssembly().CodeBase",("当前程序集的CodeBase路径", Assembly.GetExecutingAssembly()?.CodeBase) },
 
-     //已过时:入口程序集的CodeBase路径,可能为file URI格式
-     {"Assembly.GetEntryAssembly().CodeBase",("入口程序集的CodeBase路径", Assembly.GetEntryAssembly()?.CodeBase) },
- };
+        //已过时:入口程序集的CodeBase路径,可能为file URI格式
+        {"Assembly.GetEntryAssembly().CodeBase",("入口程序集的CodeBase路径", Assembly.GetEntryAssembly()?.CodeBase) },
+    };
 
- var message = string.Empty;
- foreach (var item in pathDic)
- {
-     message += $"{item.Key} => {item.Value.path}{Environment.NewLine}";
- }
+    var message = string.Empty;
+    foreach (var item in pathDic)
+    {
+        message += $"{item.Key} => {item.Value.path}{Environment.NewLine}";
+    }
 
- Console.WriteLine(message);
+    Console.WriteLine(message);
+}
diff --git a/Docs/1.2.使用准则.ipynb b/Docs/1.2.使用准则.ipynb
index 14e92d9..ee4d163 100644
--- a/Docs/1.2.使用准则.ipynb
+++ b/Docs/1.2.使用准则.ipynb
@@ -21,12 +21,12 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## 0、准备工作:先执行下面单元,以启动WebApi及设置全局对象、方法及其它"
+    "## 准备工作:先执行下面单元,以启动WebApi及设置全局对象、方法及其它"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -43,6 +43,7 @@
     "//全局设置\n",
     "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
     "\n",
+    "//全局命名空间引用\n",
     "global using System.Net;\n",
     "global using System.Net.Http;\n",
     "global using System.Diagnostics;\n",
@@ -56,13 +57,20 @@
     "global using HttpClientStudy.Core;\n",
     "global using HttpClientStudy.Core.Utilities;\n",
     "\n",
-    "var global_queryDomain = \"soft.pwidc.cn\";\n",
-    "var global_queryPort = 80;\n",
-    "var global_queryBaseUrl = $\"http://{global_queryDomain}:{global_queryPort}\";\n",
-    "\n",
-    "var global_ips = Dns.GetHostAddresses(global_queryDomain);\n",
+    "//全局共享变量\n",
+    "var global_api_config = HttpClientStudy.Config.WebApiConfigManager.GetWebApiConfig();\n",
+    "var global_ips = Dns.GetHostAddresses(global_api_config.Host);\n",
     "var global_queryIp = global_ips.First().ToString();\n",
-    "var global_netstat_filter = $\"{global_queryIp}:{global_queryPort}\";"
+    "if(global_api_config.Host.Contains(\"localhost\"))\n",
+    "{\n",
+    "    global_queryIp = \"127.0.0.1\";\n",
+    "}\n",
+    "var global_default_page = $\"{global_api_config.PathBase}/Normal/GetAllAccounts\";\n",
+    "//启动WebAPI程序\n",
+    "var global_netstat_filter = $\"{global_queryIp}:{global_api_config.Port}\";\n",
+    "var global_webapi_file = Path.GetFullPath(\"./Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.exe\", Environment.CurrentDirectory); \n",
+    "var message = AppUtility.RunWebApiExeFile(global_webapi_file);\n",
+    "Console.WriteLine(message);"
    ]
   },
   {
@@ -89,7 +97,7 @@
    "outputs": [],
    "source": [
     "{ //大括号: 1、作用域隔离 2、方便整体代码折叠\n",
-    "    Console.WriteLine(global_queryDomain);\n",
+    "    Console.WriteLine(global_api_config.BaseUrl);\n",
     "}"
    ]
   },
@@ -152,7 +160,7 @@
     "$WebAppProc = Get-Process $WebAppProcName -ErrorAction Ignore\n",
     "if($null -eq $WebAppProc)\n",
     "{\n",
-    "    Write-Host \"进程没有找到,可能已经\"\n",
+    "    Write-Host \"进程没有找到,可能已经关闭\"\n",
     "}\n",
     "else {\n",
     "    $WebAppProc.Kill();\n",
@@ -197,13 +205,16 @@
    "outputs": [],
    "source": [
     "using System.Net.Http;\n",
-    "\n",
-    "var handler = new SocketsHttpHandler\n",
     "{\n",
-    "    // Recreate every 15 minutes\n",
-    "    PooledConnectionLifetime = TimeSpan.FromMinutes(15) \n",
-    "};\n",
-    "var sharedClient = new HttpClient(handler);"
+    "    var handler = new SocketsHttpHandler\n",
+    "    {\n",
+    "        // 15分钟\n",
+    "        PooledConnectionLifetime = TimeSpan.FromMinutes(15) \n",
+    "    };\n",
+    "    var sharedClient = new HttpClient(handler);\n",
+    "\n",
+    "    sharedClient.Display();\n",
+    "}"
    ]
   },
   {
@@ -284,15 +295,19 @@
    },
    "outputs": [],
    "source": [
-    "using System.Net.Http;\n",
-    "var socketsHandler = new SocketsHttpHandler\n",
+    "//手动配置 SocketsHttpHandler\n",
     "{\n",
-    "\tPooledConnectionLifetime = TimeSpan.FromMinutes(10),\n",
-    "\tPooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),\n",
-    "\tMaxConnectionsPerServer = 10\n",
-    "};\n",
-    "\t\n",
-    "var client = new HttpClient(socketsHandler);"
+    "\tvar socketsHandler = new SocketsHttpHandler\n",
+    "\t{\n",
+    "\t\tPooledConnectionLifetime = TimeSpan.FromMinutes(10),\n",
+    "\t\tPooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),\n",
+    "\t\tMaxConnectionsPerServer = 10\n",
+    "\t};\n",
+    "\t\t\n",
+    "\tvar client = new HttpClient(socketsHandler);\n",
+    "\n",
+    "\tclient.Display();\n",
+    "}"
    ]
   },
   {
@@ -328,6 +343,8 @@
    "source": [
     "//测试连接寿命\n",
     "{\n",
+    "    Console.WriteLine(\"程序运行大约要10-20秒,请在程序退出后,执行下面命令行查看网络情况\");\n",
+    "\n",
     "    //自定义行为\n",
     "    var socketsHandler = new SocketsHttpHandler\n",
     "    {\n",
@@ -341,15 +358,24 @@
     "        MaxConnectionsPerServer = 10\n",
     "    };\n",
     "\n",
-    "    var client = new HttpClient(socketsHandler);\n",
+    "    var client = new HttpClient(socketsHandler)\n",
+    "    {\n",
+    "        BaseAddress = new Uri(global_api_config.BaseUrl)\n",
+    "    };\n",
+    "\n",
+    "    var displayer = \"\".Display();\n",
     "\n",
     "    for (var i = 0; i < 5; i++)\n",
     "    {\n",
-    "        _ = await client.GetAsync(global_queryBaseUrl);\n",
+    "        if(i>0)\n",
+    "        {\n",
+    "            await Task.Delay(TimeSpan.FromSeconds(2));\n",
+    "        }\n",
+    "        _ = await client.GetAsync(global_default_page);\n",
+    "        displayer.Update(($\"第{i+1}次请求完成\"));\n",
+    "        \n",
     "        await Task.Delay(TimeSpan.FromSeconds(2));\n",
     "    }\n",
-    "\n",
-    "    Console.WriteLine(\"程序运行大约要10-20秒,请在程序退出后,执行下面命令行查看网络情况\");\n",
     "}"
    ]
   },
@@ -381,7 +407,6 @@
     "\n",
     "Write-Host \"请先执行上面的单元,再执行本单元\"\n",
     "Write-Host \"网络状态\"\n",
-    "\n",
     "netstat -ano | findstr $queryFilter"
    ]
   },
@@ -395,7 +420,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -407,33 +432,12 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "47.103.77.66\n",
-      "程序运行大约要10-20,请在程序退出后,执行下面命令行查看网络情况\n",
-      "  TCP    192.168.1.151:14073    47.103.77.66:80        TIME_WAIT       0\n",
-      "  TCP    192.168.1.151:14079    47.103.77.66:80        TIME_WAIT       0\n",
-      "  TCP    192.168.1.151:14085    47.103.77.66:80        TIME_WAIT       0\n",
-      "  TCP    192.168.1.151:14092    47.103.77.66:80        TIME_WAIT       0\n",
-      "  TCP    192.168.1.151:14101    47.103.77.66:80        ESTABLISHED     39888\n",
-      "\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
-    "{   //\n",
-    "    var ips = await Dns.GetHostAddressesAsync(global_queryDomain);\n",
-    "    string firstIp = ips.FirstOrDefault().ToString();\n",
-    "        \n",
-    "    foreach (var ipAddress in ips)\n",
-    "    {\n",
-    "        Console.WriteLine(ipAddress.MapToIPv4().ToString());\n",
-    "    }\n",
-    "\n",
+    "//程序池设置\n",
+    "{  \n",
     "    //自定义行为\n",
+    "    Console.WriteLine(\"程序运行大约要10-20,请在程序退出后,执行下面命令行查看网络情况\");\n",
     "    var socketsHandler2 = new SocketsHttpHandler\n",
     "    {\n",
     "        PooledConnectionLifetime = TimeSpan.FromSeconds(1),\n",
@@ -441,7 +445,12 @@
     "        MaxConnectionsPerServer = 1\n",
     "    };\n",
     "\n",
-    "    var client2 = new HttpClient(socketsHandler2);\n",
+    "    var client2 = new HttpClient(socketsHandler2)\n",
+    "    {\n",
+    "        BaseAddress = new Uri(global_api_config.BaseUrl)\n",
+    "    };\n",
+    "\n",
+    "    var displayer = \"\".Display();\n",
     "\n",
     "    for (var i = 0; i < 5; i++)\n",
     "    {\n",
@@ -449,13 +458,14 @@
     "        {\n",
     "            await Task.Delay(TimeSpan.FromSeconds(2));\n",
     "        }\n",
-    "        _ = await client2.GetAsync(global_queryBaseUrl);\n",
+    "        _ = await client2.GetAsync(global_default_page);\n",
+    "        displayer.Update(($\"第{i+1}次请求完成\"));\n",
+    "        \n",
+    "        await Task.Delay(TimeSpan.FromSeconds(2));\n",
     "    }\n",
-    "\n",
-    "    Console.WriteLine(\"程序运行大约要10-20,请在程序退出后,执行下面命令行查看网络情况\");\n",
-    "\n",
+    "   \n",
     "    //调用命令行,显示查看网络情况\n",
-    "    string command = $\"netstat -ano | findstr {firstIp}\";\n",
+    "    string command = $\"netstat -ano | findstr {global_netstat_filter}\";\n",
     "    \n",
     "    // 创建一个新的ProcessStartInfo对象\n",
     "    ProcessStartInfo startInfo = new ProcessStartInfo(\"cmd\", $\"/c {command}\")\n",
@@ -514,7 +524,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -526,20 +536,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "开始请求网络...\n",
-      "共请求了200次,耗时 24989 毫秒\n",
-      "当前网络状态\n",
-      "  TCP    192.168.1.151:18018    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18019    47.103.77.66:80        ESTABLISHED     32156\n",
-      "\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "/*\n",
     "\t功能:将MaxConnectionsPerServer限制为2。然后启动200个任务,每个任务都向同一端点发出HTTP请求。这些任务将同时运行。所有请求竞争所花费的时间将写入控制台。\n",
@@ -554,11 +551,14 @@
     "\t\tMaxConnectionsPerServer = 2\n",
     "\t};\n",
     "\n",
-    "\tvar client = new HttpClient(socketsHandler);\n",
+    "\tvar client = new HttpClient(socketsHandler)\n",
+    "\t{\n",
+    "\t\tBaseAddress = new Uri(global_api_config.BaseUrl)\n",
+    "\t};\n",
     "\n",
     "\tvar sw = Stopwatch.StartNew();\n",
     "\n",
-    "\tvar tasks = Enumerable.Range(0, 200).Select(i => client.GetAsync(global_queryBaseUrl));\n",
+    "\tvar tasks = Enumerable.Range(0, 200).Select(i => client.GetAsync(global_default_page));\n",
     "\n",
     "\tawait Task.WhenAll(tasks);\n",
     "\n",
@@ -575,7 +575,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "pwsh"
@@ -587,16 +587,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "  TCP    192.168.1.151:18018    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18019    47.103.77.66:80        ESTABLISHED     32156\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "# 重新查询当前网络状态\n",
     "#!set --value @csharp:global_netstat_filter --name queryFilter\n",
@@ -612,7 +603,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -624,30 +615,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "开始请求网络...\n",
-      "共请求了200次,耗时 11781 毫秒\n",
-      "当前网络状态\n",
-      "  TCP    192.168.1.151:18018    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18019    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18167    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18168    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18169    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18170    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18171    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18172    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18173    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18174    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18175    47.103.77.66:80        ESTABLISHED     32156\n",
-      "  TCP    192.168.1.151:18176    47.103.77.66:80        ESTABLISHED     32156\n",
-      "\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "{   //MaxConnectionsPerServer 设置为10:网络连接将增加到10个,耗时将减少到1/4\n",
     "\tConsole.WriteLine(\"开始请求网络...\");\n",
@@ -658,11 +626,16 @@
     "\t\tMaxConnectionsPerServer = 10\n",
     "\t};\n",
     "\n",
-    "\tvar client = new HttpClient(socketsHandler);\n",
+    "\tvar client = new HttpClient(socketsHandler)\n",
+    "\t{\n",
+    "\t\tBaseAddress = new Uri(global_api_config.BaseUrl)\n",
+    "\t};\n",
+    "\n",
+    "\t//client.Display();\n",
     "\n",
     "\tvar sw = Stopwatch.StartNew();\n",
     "\n",
-    "\tvar tasks = Enumerable.Range(0, 200).Select(i => client.GetAsync(global_queryBaseUrl));\n",
+    "\tvar tasks = Enumerable.Range(0, 200).Select(i => client.GetAsync(global_default_page));\n",
     "\n",
     "\tawait Task.WhenAll(tasks);\n",
     "\n",
@@ -714,7 +687,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -738,7 +711,7 @@
     "   {\n",
     "        using (var client = new HttpClient())\n",
     "        {\n",
-    "            _ = await client.GetAsync(global_queryBaseUrl);\n",
+    "            _ = await client.GetAsync (global_api_config.BaseUrl + global_default_page);\n",
     "        }   \n",
     "        Interlocked.Add(ref requestCount, 1);\n",
     "    });\n",
@@ -747,10 +720,12 @@
     "{ //使用长期客户端\n",
     "   using (var client = new HttpClient())\n",
     "   {\n",
+    "        client.BaseAddress = new Uri(global_api_config.BaseUrl);\n",
+    "        \n",
     "        for(int i=0; i<10; i++)\n",
     "        {\n",
     "            //n次调用,均使用同一个 HttpClient 实例\n",
-    "            _ = await client.GetAsync(global_queryBaseUrl);\n",
+    "            _ = await client.GetAsync(global_default_page);\n",
     "        }\n",
     "   }// 所有调用完成,才释放 HttpClient 实例\n",
     "}"
@@ -765,7 +740,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -777,24 +752,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.Http.Resilience, 8.6.0</span></li><li><span>Polly, 8.4.1</span></li></ul></div></div>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "共有227个字符\r\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "#r \"nuget:Polly\"\n",
     "#r \"nuget:Microsoft.Extensions.Http.Resilience\"\n",
@@ -826,8 +784,9 @@
     "    #pragma warning restore EXTEXP0001\n",
     "\n",
     "    var httpClient = new HttpClient(resilienceHandler);\n",
+    "    httpClient.BaseAddress = new Uri(global_api_config.BaseUrl);\n",
     "\n",
-    "    var response = await httpClient.GetAsync(\"https://www.baidu.com\");\n",
+    "    var response = await httpClient.GetAsync(global_default_page);\n",
     "    var htmlText = await response.Content.ReadAsStringAsync();\n",
     "    Console.WriteLine($\"共有{htmlText.Length}个字符\");\n",
     "}\n"
diff --git a/Docs/1.6.测试.ipynb b/Docs/1.6.测试.ipynb
index afae443..2c5474b 100644
--- a/Docs/1.6.测试.ipynb
+++ b/Docs/1.6.测试.ipynb
@@ -33,7 +33,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 25,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -45,14 +45,50 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "1.0项目管理.dib\n",
+      "1.1.概述.ipynb\n",
+      "1.2.使用准则.ipynb\n",
+      "1.3.0.基础使用.管理客户端.ipynb\n",
+      "1.3.1.基础使用.发送请求.ipynb\n",
+      "1.3.2.基础使用.使用请求体.ipynb\n",
+      "1.3.3.基础使用.处理响应.ipynb\n",
+      "1.3.4.基础使用.处理错误.ipynb\n",
+      "1.3.5.基础使用.使用代理.ipynb\n",
+      "1.3.6.基础使用.使用Json.ipynb\n",
+      "1.3.7.基础使用.使用Cookie.ipynb\n",
+      "1.4.0.高级使用.概述.ipynb\n",
+      "1.4.1.高级使用.初始化.ipynb\n",
+      "1.4.2.高级使用.连接池.ipynb\n",
+      "1.4.3.高级使用.重复使用.ipynb\n",
+      "1.4.4.高级使用.使用管道.ipynb\n",
+      "1.4.5.高级使用.类型化客户端.ipynb\n",
+      "1.4.6..高级使用.工厂模式.ipynb\n",
+      "1.4.7.高级使用.Polly.ipynb\n",
+      "1.5.总结.ipynb\n",
+      "1.6.测试.ipynb\n",
+      "2.1.内核中的各种路径.ipynb\n",
+      "Assets\n",
+      "Publish\n",
+      "学习.ps1\n",
+      "说明.md\n",
+      "\r\n"
+     ]
+    }
+   ],
    "source": [
     "//引用项目\n",
     "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
     "\n",
     "//执行C#工具方法\n",
     "using HttpClientStudy.Core.Utilities;\n",
-    "var result = CmdUtility.RunCmd(\"dir\");\n",
+    "\n",
+    "\n",
+    "var result = AppUtility.RunCmd(\"ls\");\n",
     "Console.WriteLine(result);"
    ]
   },
@@ -65,7 +101,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 26,
    "metadata": {
     "dotnet_interactive": {
      "language": "pwsh"
@@ -85,7 +121,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 31,
    "metadata": {
     "dotnet_interactive": {
      "language": "pwsh"
@@ -105,7 +141,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 53,
    "metadata": {
     "dotnet_interactive": {
      "language": "pwsh"
@@ -117,19 +153,35 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "进程没有找到,可能已经关闭\r\n"
+     ]
+    },
+    {
+     "ename": "Error",
+     "evalue": "Command failed: SubmitCode: # 关闭项目进程 ...",
+     "output_type": "error",
+     "traceback": [
+      "Command failed: SubmitCode: # 关闭项目进程 ..."
+     ]
+    }
+   ],
    "source": [
     "# 关闭项目进程\n",
     "$WebAppProcName =\"HttpClientStudy.WebApp\";\n",
     "$WebAppProc = Get-Process $WebAppProcName -ErrorAction Ignore\n",
     "if($null -eq $WebAppProc)\n",
     "{\n",
-    "    Write-Host \"进程没有找到,可能已经\"\n",
+    "    Write-Host \"进程没有找到,可能已经关闭\"\n",
     "}\n",
     "else {\n",
     "    $WebAppProc.Kill();\n",
     "    Write-Host \"$WebAppProcName 进程已退出\"\n",
-    "}\n"
+    "}"
    ]
   },
   {
@@ -141,7 +193,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 47,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -153,7 +205,24 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "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"
+     ]
+    }
+   ],
    "source": [
     "//启动已发布的WebApi项目\n",
     "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
@@ -162,7 +231,7 @@
     "    var file = System.IO.Path.GetFullPath(\"./Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.exe\",System.Environment.CurrentDirectory);\n",
     "\n",
     "    file.Display();\n",
-    "    var message = HttpClientStudy.Core.Utilities.DotnetCommondUtility.RunWebApp(file);\n",
+    "    var message = HttpClientStudy.Core.Utilities.AppUtility.RunWebApiExeFile(file);\n",
     "\n",
     "    Console.WriteLine(message);\n",
     "}"
@@ -170,7 +239,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 46,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -182,14 +251,24 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "配置文件根目录:c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
+      "WebApi程序[进程号:,进程名:],已关闭!\n"
+     ]
+    }
+   ],
    "source": [
     "//关闭项目进程\n",
     "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
+    "\n",
     "{\n",
-    "    var message = HttpClientStudy.Core.Utilities.DotnetCommondUtility.SopWebApp();\n",
+    "    var message = HttpClientStudy.Core.Utilities.AppUtility.StopWebApiExeFile();\n",
     "    Console.WriteLine(message);\n",
-    "}\n"
+    "}"
    ]
   }
  ],
diff --git a/HttpClientStudy.Config/ConfigFiles/Config.json b/HttpClientStudy.Config/ConfigFiles/Config.json
index 007c557..d09e326 100644
--- a/HttpClientStudy.Config/ConfigFiles/Config.json
+++ b/HttpClientStudy.Config/ConfigFiles/Config.json
@@ -1,6 +1,9 @@
 {
   "WebApi": {
-    "BaseUrl": "http://localhost:5189",
+    "Scheme": "http",
+    "Host": "127.0.0.1",
+    "Port": "5189",
+    "PathBase": "/api",
     "WebAppMutexName": "HttpClientStudy.WebApp"
   } 
 }
\ No newline at end of file
diff --git a/HttpClientStudy.Config/WebApiConfig.cs b/HttpClientStudy.Config/WebApiConfig.cs
index 6758831..d24c20b 100644
--- a/HttpClientStudy.Config/WebApiConfig.cs
+++ b/HttpClientStudy.Config/WebApiConfig.cs
@@ -2,8 +2,37 @@
 {
     public class WebApiConfig
     {
-        public string BaseUrl { get; set; } = "http://localhost:5189";
+        /// <summary>
+        /// 主机
+        /// </summary>
+        public string Host { get; set; } = "localhost";
 
+        /// <summary>
+        /// 端口
+        /// </summary>
+        public int Port { get; set; } = 5189;
+
+        /// <summary>
+        /// 协议(http 或者 https)
+        /// </summary>
+        public string Scheme { get; set; } = "http";
+
+        /// <summary>
+        /// 基本路径,不以/结尾
+        /// </summary>
+        public string PathBase { get; set; } = string.Empty;
+        
+        /// <summary>
+        /// 
+        /// </summary>
+        public string BaseUrl 
+        {
+            get { return $"{Scheme}://{Host}:{Port}"; }
+        }
+
+        /// <summary>
+        /// WebApi 互斥量
+        /// </summary>
         public string WebAppMutexName { get; set; } = "HttpClientStudy.WebApp";
     }
 }
diff --git a/HttpClientStudy.UnitTest/ConfigTest/WebApiConfigTest.cs b/HttpClientStudy.UnitTest/ConfigTest/WebApiConfigTest.cs
index ed933fb..eae1b14 100644
--- a/HttpClientStudy.UnitTest/ConfigTest/WebApiConfigTest.cs
+++ b/HttpClientStudy.UnitTest/ConfigTest/WebApiConfigTest.cs
@@ -50,5 +50,14 @@ namespace HttpClientStudy.UnitTest.ConfigTest
             Assert.NotNull(webApiConfig);
             Assert.NotEmpty(webApiConfig.WebAppMutexName);
         }
+
+        [Fact]
+        public void DNS_Test()
+        {
+
+            var ips = Dns.GetHostAddresses(WebApiConfigManager.GetWebApiConfig().Host);
+
+            Assert.NotEmpty(ips);
+        }
     }
 }
diff --git a/HttpClientStudy.UnitTest/HttpClients/BaseHttpClientTest.cs b/HttpClientStudy.UnitTest/HttpClients/BaseHttpClientTest.cs
index 6cf23ab..446e85e 100644
--- a/HttpClientStudy.UnitTest/HttpClients/BaseHttpClientTest.cs
+++ b/HttpClientStudy.UnitTest/HttpClients/BaseHttpClientTest.cs
@@ -1,4 +1,6 @@
-namespace HttpClientStudy.UnitTest.HttpClients
+using Microsoft.AspNetCore.Http;
+
+namespace HttpClientStudy.UnitTest.HttpClients
 {
     /// <summary>
     ///  SimpleHttpClient 测试类
@@ -36,5 +38,20 @@
             Assert.IsType<string>(result.Message);
             Assert.NotEmpty(result.Message);
         }
+
+        [Fact]
+        public void Study_Test()
+        {
+            BaseHttpClient client = new BaseHttpClient();
+
+            var result = client.GetJson<BaseResult<string>>(WebApiConfigManager.GetWebApiConfig().BaseUrl + "/api/Simple/GetAccount");
+
+            Assert.NotNull(result);
+            Assert.IsAssignableFrom<BaseResult>(result);
+            Assert.Equal(1, result.Code);
+            Assert.Contains("成功", result.Message);
+            Assert.IsType<string>(result.Message);
+            Assert.NotEmpty(result.Message);
+        }
     }
 }
diff --git a/HttpClientStudy.WebApp/appsettings.json b/HttpClientStudy.WebApp/appsettings.json
index 1885915..5585347 100644
--- a/HttpClientStudy.WebApp/appsettings.json
+++ b/HttpClientStudy.WebApp/appsettings.json
@@ -3,7 +3,7 @@
   "Logging": {
     "LogLevel": {
       "Default": "Information",
-      "Microsoft.AspNetCore": "Warning"
+      "Microsoft.AspNetCore": "Information"
     }
   },
   "AllowedHosts": "*"