diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb
index 6885467..2732b8c 100644
--- a/Docs/1.3.0.基础使用.管理客户端.ipynb
+++ b/Docs/1.3.0.基础使用.管理客户端.ipynb
@@ -23,9 +23,16 @@
"为实现复用,HttpClient类库默认使用连接池和请求管道,可以手动管理(连接池、配置管道、使用Polly); 结合IoC容器、工厂模式(提供了IHttpClientFactory类库)、复原库Polly,可以更加方便、完善的使用,这也是推荐的方法。"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 0、初始化与全局设置"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 67,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -41,7 +48,7 @@
{
"data": {
"text/html": [
- "
Installed Packages- Microsoft.Extensions.DependencyInjection, 8.0.0
- System.Net.Http.Json, 8.0.0
"
+ "Installed Packages- Microsoft.Extensions.DependencyInjection, 8.0.0
- Microsoft.Extensions.Http, 8.0.0
- Microsoft.Extensions.Http.Polly, 8.0.7
- Polly, 8.4.1
- System.Net.Http.Json, 8.0.0
"
]
},
"metadata": {},
@@ -57,7 +64,7 @@
{
"data": {
"text/plain": [
- "d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe"
+ "e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe"
]
},
"metadata": {},
@@ -67,18 +74,53 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "程序[d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n"
+ "程序[e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n"
]
}
],
"source": [
"//全局设置,行运行一次,为后续准备\n",
"#r \"nuget:System.Net.Http.Json\"\n",
+ "#r \"nuget:Microsoft.Extensions.Http\"\n",
"#r \"nuget:Microsoft.Extensions.DependencyInjection\"\n",
+ "#r \"nuget:Polly\"\n",
+ "#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
"#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
"\n",
+ "global using System;\n",
+ "global using System.IO;\n",
+ "global using System.IO.Enumeration;\n",
+ "global using System.Buffers;\n",
+ "\n",
+ "global using System.Collections;\n",
+ "global using System.Collections.Concurrent;\n",
+ "global using System.Linq;\n",
+ "global using System.Linq.Expressions;\n",
+ "global using System.Threading;\n",
+ "global using System.Threading.Tasks;\n",
+ "\n",
"global using System.Net.Http;\n",
"global using System.Net.Http.Headers;\n",
+ "\n",
+ "global using Microsoft.Extensions.DependencyInjection;\n",
+ "global using Microsoft.Extensions.DependencyInjection.Extensions;\n",
+ "\n",
+ "global using Polly;\n",
+ "global using Polly.NoOp;\n",
+ "global using Polly.Simmy;\n",
+ "global using Polly.Retry;\n",
+ "global using Polly.Hedging;\n",
+ "global using Polly.Timeout;\n",
+ "global using Polly.Bulkhead;\n",
+ "global using Polly.Fallback;\n",
+ "global using Polly.RateLimit;\n",
+ "global using Polly.CircuitBreaker;\n",
+ "global using Polly.Utilities;\n",
+ "global using Polly.Extensions;\n",
+ "global using Polly.Wrap;\n",
+ "global using Polly.Registry;\n",
+ "global using Polly.Telemetry;\n",
+ "\n",
"global using HttpClientStudy.Config;\n",
"global using HttpClientStudy.Core;\n",
"global using HttpClientStudy.Core.Utilities;\n",
@@ -127,7 +169,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -139,24 +181,7 @@
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\r\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "第 10 次/ 共 10 次请求,成功!"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
+ "outputs": [],
"source": [
"/*\n",
" 每次请求都实例化:并发量大、请求频繁进会耗尽套接字端口\n",
@@ -209,7 +234,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -221,17 +246,7 @@
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
- "{\"data\":{\"host\":\"localhost\",\"port\":5189,\"scheme\":\"http\",\"pathBase\":\"\",\"baseUrl\":\"http://localhost:5189\",\"webAppMutexName\":\"HttpClientStudy.WebApp\"},\"code\":1,\"message\":\"成功\"}\n",
- "{\"data\":[{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":3,\"name\":\"管理员03\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":4,\"name\":\"管理员04\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":5,\"name\":\"管理员05\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":6,\"name\":\"管理员06\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":7,\"name\":\"管理员07\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":8,\"name\":\"管理员08\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":9,\"name\":\"管理员09\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":10,\"name\":\"管理员10\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":11,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":12,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":13,\"name\":\"开发03\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":14,\"name\":\"开发04\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":15,\"name\":\"开发05\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":16,\"name\":\"开发06\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":17,\"name\":\"开发07\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":18,\"name\":\"开发08\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":19,\"name\":\"开发09\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":20,\"name\":\"开发10\",\"password\":\"123456\",\"role\":\"Dev\"}],\"code\":1,\"message\":\"成功\"}\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"/*\n",
" 静态类/属性\n",
@@ -288,7 +303,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -300,18 +315,7 @@
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
- "HttpClientSingleton 初始化一次\n",
- "{\"data\":{\"host\":\"localhost\",\"port\":5189,\"scheme\":\"http\",\"pathBase\":\"\",\"baseUrl\":\"http://localhost:5189\",\"webAppMutexName\":\"HttpClientStudy.WebApp\"},\"code\":1,\"message\":\"成功\"}\n",
- "{\"data\":[{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":3,\"name\":\"管理员03\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":4,\"name\":\"管理员04\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":5,\"name\":\"管理员05\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":6,\"name\":\"管理员06\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":7,\"name\":\"管理员07\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":8,\"name\":\"管理员08\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":9,\"name\":\"管理员09\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":10,\"name\":\"管理员10\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":11,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":12,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":13,\"name\":\"开发03\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":14,\"name\":\"开发04\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":15,\"name\":\"开发05\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":16,\"name\":\"开发06\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":17,\"name\":\"开发07\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":18,\"name\":\"开发08\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":19,\"name\":\"开发09\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":20,\"name\":\"开发10\",\"password\":\"123456\",\"role\":\"Dev\"}],\"code\":1,\"message\":\"成功\"}\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"/*\n",
" 单例实现1:\n",
@@ -384,7 +388,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -396,18 +400,7 @@
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
- "HttpClientSingleton2 初始化一次\n",
- "{\"data\":{\"host\":\"localhost\",\"port\":5189,\"scheme\":\"http\",\"pathBase\":\"\",\"baseUrl\":\"http://localhost:5189\",\"webAppMutexName\":\"HttpClientStudy.WebApp\"},\"code\":1,\"message\":\"成功\"}\n",
- "{\"data\":[{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":3,\"name\":\"管理员03\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":4,\"name\":\"管理员04\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":5,\"name\":\"管理员05\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":6,\"name\":\"管理员06\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":7,\"name\":\"管理员07\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":8,\"name\":\"管理员08\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":9,\"name\":\"管理员09\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":10,\"name\":\"管理员10\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":11,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":12,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":13,\"name\":\"开发03\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":14,\"name\":\"开发04\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":15,\"name\":\"开发05\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":16,\"name\":\"开发06\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":17,\"name\":\"开发07\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":18,\"name\":\"开发08\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":19,\"name\":\"开发09\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":20,\"name\":\"开发10\",\"password\":\"123456\",\"role\":\"Dev\"}],\"code\":1,\"message\":\"成功\"}\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"/*\n",
" 单例实现2:\n",
@@ -502,7 +495,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -514,15 +507,7 @@
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "401173\r\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"// 百度服务类\n",
"public sealed class BaiduService \n",
@@ -564,7 +549,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -576,16 +561,7 @@
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
- "{\"data\":\"操作成功\",\"code\":1,\"message\":\"成功\"}\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"// 本机服务类\n",
"// 百度服务类\n",
@@ -635,7 +611,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -647,77 +623,228 @@
"languageId": "polyglot-notebook"
}
},
+ "outputs": [],
+ "source": [
+ "#r \"nuget:Polly\"\n",
+ "#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
+ "\n",
+ "using Polly;\n",
+ "using Polly.Simmy;\n",
+ "using Polly.Retry;\n",
+ "using Polly.Extensions;\n",
+ "{\n",
+ " var pipleLine = new ResiliencePipelineBuilder()\n",
+ " .AddRetry(new RetryStrategyOptions()\n",
+ " {\n",
+ " ShouldHandle = new PredicateBuilder().Handle(),\n",
+ " MaxRetryAttempts = 3, // Retry up to 3 times\n",
+ " OnRetry = args =>\n",
+ " {\n",
+ " // Due to how we have defined ShouldHandle, this delegate is called only if an exception occurred.\n",
+ " // Note the ! sign (null-forgiving operator) at the end of the command.\n",
+ " var exception = args.Outcome.Exception!; // The Exception property is nullable\n",
+ " Console.WriteLine(\"内部重试\");\n",
+ " return default;\n",
+ " }\n",
+ " })\n",
+ " .Build();\n",
+ "\n",
+ " var BaseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
+ " HttpClient client = new HttpClient(new SocketsHttpHandler(){})\n",
+ " {\n",
+ " BaseAddress = new Uri(BaseUrl),\n",
+ " };\n",
+ "\n",
+ " try\n",
+ " {\n",
+ " await pipleLine.ExecuteAsync(async (inneerToken)=>\n",
+ " {\n",
+ " var response = await client.GetAsync(\"api/Polly8/Retry_Exception\",inneerToken);\n",
+ " response.EnsureSuccessStatusCode();\n",
+ " });\n",
+ " }\n",
+ " catch(Exception ex)\n",
+ " {\n",
+ " Console.WriteLine(ex.Message);\n",
+ " }\n",
+ " finally\n",
+ " {\n",
+ "\n",
+ " }\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 5、IoC容器管理"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 直接注册IoC"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "vscode": {
+ "languageId": "polyglot-notebook"
+ }
+ },
"outputs": [
- {
- "data": {
- "text/html": [
- "Installed Packages- Microsoft.Extensions.Http.Polly, 8.0.7
- Polly, 8.4.1
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
{
"name": "stdout",
"output_type": "stream",
"text": [
- "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
- "内部重试\n",
- "内部重试\n",
- "内部重试\n",
- "Response status code does not indicate success: 500 (Internal Server Error).\n"
+ "9193\n",
+ "9193\n",
+ "18\n"
]
}
],
"source": [
- "#r \"nuget:Polly\"\n",
- "#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
+ "/*\n",
+ " 注意:\n",
+ " 1、直接IoC管理:只能一个,不太方便;\n",
+ " 2、可使用.NET 8+ 的 KeyedService, 可以管理多个。 老版只能用服务集合,勉强能用;\n",
+ " 3、把HttpClient 放在多个类中,分别注册使用;不过这样,不如直接使用类型化客户端;\n",
+ "*/\n",
+ "{ // 直接使用\n",
+ " var services = new ServiceCollection();\n",
+ " services.AddSingleton(new HttpClient()\n",
+ " {\n",
+ " //BaseAddress = new Uri(\"https://localhost:5001/\"),\n",
+ " Timeout = TimeSpan.FromSeconds(10),\n",
+ " });\n",
"\n",
- "using Polly;\n",
- "using Polly.Simmy;\n",
- "using Polly.Retry;\n",
- "using Polly.Extensions;\n",
+ " var client = services.BuildServiceProvider().GetRequiredService();\n",
"\n",
- "var pipleLine = new ResiliencePipelineBuilder()\n",
- " .AddRetry(new RetryStrategyOptions()\n",
- " {\n",
- " ShouldHandle = new PredicateBuilder().Handle(),\n",
- " MaxRetryAttempts = 3, // Retry up to 3 times\n",
- " OnRetry = args =>\n",
+ " var resp = await client.GetAsync(\"https://www.baidu.com\");\n",
+ " resp.EnsureSuccessStatusCode();\n",
+ "\n",
+ " var content = await resp.Content.ReadAsStringAsync();\n",
+ " Console.WriteLine(content.Length);\n",
+ "}\n",
+ "\n",
+ "{ // KeyService: .Net 8+ 才支持的功能\n",
+ " var services = new ServiceCollection();\n",
+ " services\n",
+ " .AddKeyedSingleton(\"HttpClientA\",new HttpClient()\n",
" {\n",
- " // Due to how we have defined ShouldHandle, this delegate is called only if an exception occurred.\n",
- " // Note the ! sign (null-forgiving operator) at the end of the command.\n",
- " var exception = args.Outcome.Exception!; // The Exception property is nullable\n",
- " Console.WriteLine(\"内部重试\");\n",
- " return default;\n",
- " }\n",
- " })\n",
- " .Build();\n",
+ " BaseAddress = new Uri(\"https://www.baidu.com/\"),\n",
+ " Timeout = TimeSpan.FromSeconds(10),\n",
+ " })\n",
+ " .AddKeyedSingleton(\"HttpClientB\", new HttpClient()\n",
+ " {\n",
+ " BaseAddress = new Uri(\"https://www.qq.com/\"),\n",
+ " Timeout = TimeSpan.FromSeconds(2),\n",
+ " });\n",
"\n",
- "var BaseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
- "HttpClient client = new HttpClient(new SocketsHttpHandler()\n",
- "{\n",
- " \n",
- "})\n",
- "{\n",
- " BaseAddress = new Uri(BaseUrl),\n",
- "};\n",
+ " var clientA = services.BuildServiceProvider().GetRequiredKeyedService(\"HttpClientA\");\n",
+ " var responseA = await clientA.GetAsync(\"/\");\n",
+ " responseA.EnsureSuccessStatusCode();\n",
+ " var contentA = await responseA.Content.ReadAsStringAsync();\n",
+ " Console.WriteLine(contentA.Length);\n",
+ "\n",
+ " var clientB = services.BuildServiceProvider().GetRequiredKeyedService(\"HttpClientB\");\n",
+ "\n",
+ " var responseB = await clientB.GetAsync(\"/\");\n",
+ " responseB.EnsureSuccessStatusCode();\n",
+ "\n",
+ " var contentB= await responseB.Content.ReadAsStringAsync();\n",
+ " Console.WriteLine(contentB.Length);\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### HttpClient 多服务类"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {
+ "vscode": {
+ "languageId": "polyglot-notebook"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "A: 9508\n",
+ "A: 18\n"
+ ]
+ }
+ ],
+ "source": [
+ "// IoC 多个HttpClient服务类\n",
"\n",
- "try\n",
+ "public class HttpClientServerA\n",
"{\n",
- " await pipleLine.ExecuteAsync(async (inneerToken)=>\n",
+ " public static HttpClient Client = new HttpClient()\n",
+ " {\n",
+ " BaseAddress = new Uri(\"https://www.baidu.com/\"),\n",
+ " Timeout = TimeSpan.FromSeconds(2),\n",
+ " };\n",
+ "\n",
+ " public int GetBaiduIndexLength()\n",
" {\n",
- " var response = await client.GetAsync(\"api/Polly8/Retry_Exception\",inneerToken);\n",
+ " var requestMessage = new HttpRequestMessage(HttpMethod.Get, \"/\");\n",
+ "\n",
+ " var response = Client.Send(requestMessage);\n",
+ "\n",
" response.EnsureSuccessStatusCode();\n",
- " });\n",
+ "\n",
+ " var s = response.Content.ReadAsStream();\n",
+ " return (int)s.Length;\n",
+ " }\n",
"}\n",
- "catch(Exception ex)\n",
+ "\n",
+ "public class HttpClientServerB\n",
"{\n",
- " Console.WriteLine(ex.Message);\n",
+ " public static HttpClient Client = new HttpClient()\n",
+ " {\n",
+ " BaseAddress = new Uri(\"https://www.qq.com/\"),\n",
+ " Timeout = TimeSpan.FromSeconds(2),\n",
+ " };\n",
+ "\n",
+ " public int GetBaiduIndexLength()\n",
+ " {\n",
+ " var requestMessage = new HttpRequestMessage(HttpMethod.Get, \"/\");\n",
+ "\n",
+ " var response = Client.Send(requestMessage);\n",
+ "\n",
+ " response.EnsureSuccessStatusCode();\n",
+ "\n",
+ " var s = response.Content.ReadAsStream();\n",
+ " return (int)s.Length;\n",
+ " }\n",
"}\n",
- "finally\n",
+ "\n",
"{\n",
+ " var services = new ServiceCollection();\n",
+ " services.AddScoped();\n",
+ " services.AddScoped();\n",
+ "\n",
+ " var provider = services.BuildServiceProvider();\n",
+ "\n",
+ " var clientA = provider.GetService();\n",
+ " var sumA = clientA.GetBaiduIndexLength();\n",
+ " Console.WriteLine($\"A: {sumA}\");\n",
"\n",
+ " var clientB = provider.GetService();\n",
+ " var sumB = clientB.GetBaiduIndexLength();\n",
+ " Console.WriteLine($\"A: {sumB}\");\n",
"}"
]
},
@@ -725,14 +852,67 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## 5、IoC容器管理:类型化的客户端"
+ "## 6、客户端工厂管理:IHttpClientFactory(需要结合IoC) `强力推荐`"
]
},
{
"cell_type": "markdown",
- "metadata": {},
+ "metadata": {
+ "vscode": {
+ "languageId": "polyglot-notebook"
+ }
+ },
"source": [
- "## 6、客户端工厂管理:IHttpClientFactory(需要结合IoC)"
+ "使用 IHttpClientFactory 创建和管理 `短期HttpClient` 是官方强力推荐的方式。特别是使用IoC或是 ASP.NET中后台调用其它接口的情况。\n",
+ "\n",
+ "IHttpClientFactory 综合使用了 HttpClient的多种特性:HttpClient的生命周期、HttpClient的配置、HttpClient的拦截器、HttpClient的缓存、HttpClient的依赖注入、Polly等等。\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {
+ "vscode": {
+ "languageId": "polyglot-notebook"
+ }
+ },
+ "outputs": [
+ {
+ "ename": "Error",
+ "evalue": "System.InvalidOperationException: An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set.\r\n at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)\r\n at System.Net.Http.HttpClient.GetAsync(String requestUri)\r\n at Submission#69.<>d__0.MoveNext()\r\n--- End of stack trace from previous location ---\r\n at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)",
+ "output_type": "error",
+ "traceback": [
+ "System.InvalidOperationException: An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set.\r\n",
+ " at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)\r\n",
+ " at System.Net.Http.HttpClient.GetAsync(String requestUri)\r\n",
+ " at Submission#69.<>d__0.MoveNext()\r\n",
+ "--- End of stack trace from previous location ---\r\n",
+ " at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)"
+ ]
+ }
+ ],
+ "source": [
+ "//基础使用\n",
+ "{\n",
+ " var services = new ServiceCollection();\n",
+ " services.AddHttpClient((provider, client) => \n",
+ " {\n",
+ " client.BaseAddress = new Uri(\"https://www.baidu.com\");\n",
+ " });\n",
+ "\n",
+ " var provider = services.BuildServiceProvider();\n",
+ "\n",
+ " var factory = provider.GetRequiredService();\n",
+ "\n",
+ " var sum = factory.CreateClient().GetAsync(\"/\").Result.Content.ReadAsStringAsync().Result.Length;\n",
+ " \n",
+ " Console.WriteLine(sum);\n",
+ "}\n",
+ "\n",
+ "//\n",
+ "{\n",
+ "\n",
+ "}"
]
},
{
diff --git a/HttpClientStudy.Core/HttpClientStudy.Core.csproj b/HttpClientStudy.Core/HttpClientStudy.Core.csproj
index fe133c2..0092c2e 100644
--- a/HttpClientStudy.Core/HttpClientStudy.Core.csproj
+++ b/HttpClientStudy.Core/HttpClientStudy.Core.csproj
@@ -7,6 +7,7 @@
+
diff --git a/HttpClientStudy.UnitTest/TempTest.cs b/HttpClientStudy.UnitTest/TempTest.cs
index eed1aa1..60120c8 100644
--- a/HttpClientStudy.UnitTest/TempTest.cs
+++ b/HttpClientStudy.UnitTest/TempTest.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
using HttpClientStudy.Core.Utilities;
+using Microsoft.Extensions.DependencyInjection;
+
namespace HttpClientStudy.UnitTest
{
///
@@ -20,9 +22,53 @@ namespace HttpClientStudy.UnitTest
}
[Fact]
- public void Test()
+ public async Task TestAsync()
+ {
+ var services = new ServiceCollection();
+ services.AddKeyedSingleton("HttpClientA",new HttpClient()
+ {
+ //BaseAddress = new Uri("https://localhost:5001/"),
+ Timeout = TimeSpan.FromSeconds(10),
+ });
+
+ var client = services.BuildServiceProvider().GetRequiredKeyedService("HttpClientA");
+
+ var resp = await client.GetAsync("https://www.baidu.com");
+ resp.EnsureSuccessStatusCode();
+
+ var content = await resp.Content.ReadAsStringAsync();
+ Console.WriteLine(content.Length);
+ }
+
+ [Fact]
+ public async Task Temp_TestAsync()
{
-
+ var services = new ServiceCollection();
+ //services.AddHttpClient();
+ services
+ .AddHttpClient(config =>
+ {
+ config.BaseAddress = new Uri("https://www.baidu.com");
+ })
+ .ConfigureHttpClient(config =>
+ {
+ config.BaseAddress = new Uri("https://www.qq.com");
+ });
+
+
+
+ var provider = services.BuildServiceProvider();
+
+ var factory = provider.GetRequiredService();
+
+ var client = factory.CreateClient();
+
+ var res = await client.GetAsync("https://www.qq.com");
+ res.EnsureSuccessStatusCode();
+ var content = await res.Content.ReadAsStringAsync();
+
+ var sum = content.Length;
+ _output.WriteLine(sum.ToString());
}
}
}