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
" + "
Installed Packages
" ] }, "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()); } } }