main
bicijinlian 9 months ago
parent 26d64aecc6
commit 975e8d7713

@ -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": [
"<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>"
"<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>System.Net.Http.Json, 8.0.0</span></li></ul></div></div>"
]
},
"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<Exception>(),\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": [
"<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.Http.Polly, 8.0.7</span></li><li><span>Polly, 8.4.1</span></li></ul></div></div>"
]
},
"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<HttpClient>(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<HttpClient>();\n",
"\n",
"var pipleLine = new ResiliencePipelineBuilder()\n",
" .AddRetry(new RetryStrategyOptions()\n",
" {\n",
" ShouldHandle = new PredicateBuilder().Handle<Exception>(),\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<HttpClient>(\"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<HttpClient>(\"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<HttpClient>(\"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<HttpClient>(\"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<HttpClientServerA>();\n",
" services.AddScoped<HttpClientServerB>();\n",
"\n",
" var provider = services.BuildServiceProvider();\n",
"\n",
" var clientA = provider.GetService<HttpClientServerA>();\n",
" var sumA = clientA.GetBaiduIndexLength();\n",
" Console.WriteLine($\"A: {sumA}\");\n",
"\n",
" var clientB = provider.GetService<HttpClientServerB>();\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.<<Initialize>>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.<<Initialize>>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<HttpClient>((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<IHttpClientFactory>();\n",
"\n",
" var sum = factory.CreateClient().GetAsync(\"/\").Result.Content.ReadAsStringAsync().Result.Length;\n",
" \n",
" Console.WriteLine(sum);\n",
"}\n",
"\n",
"//\n",
"{\n",
"\n",
"}"
]
},
{

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.1" />
<PackageReference Include="Polly" Version="8.2.1" />
<PackageReference Include="Polly.Caching.IDistributedCache" Version="1.0.4" />

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using HttpClientStudy.Core.Utilities;
using Microsoft.Extensions.DependencyInjection;
namespace HttpClientStudy.UnitTest
{
/// <summary>
@ -20,9 +22,53 @@ namespace HttpClientStudy.UnitTest
}
[Fact]
public void Test()
public async Task TestAsync()
{
var services = new ServiceCollection();
services.AddKeyedSingleton<HttpClient>("HttpClientA",new HttpClient()
{
//BaseAddress = new Uri("https://localhost:5001/"),
Timeout = TimeSpan.FromSeconds(10),
});
var client = services.BuildServiceProvider().GetRequiredKeyedService<HttpClient>("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<HttpClient>(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<IHttpClientFactory>();
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());
}
}
}

Loading…
Cancel
Save