diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb
index 2732b8c..c2694db 100644
--- a/Docs/1.3.0.基础使用.管理客户端.ipynb
+++ b/Docs/1.3.0.基础使用.管理客户端.ipynb
@@ -32,7 +32,7 @@
},
{
"cell_type": "code",
- "execution_count": 67,
+ "execution_count": 5,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@@ -48,7 +48,7 @@
{
"data": {
"text/html": [
- "
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
"
+ "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
- Refit, 7.1.2
- Refit.HttpClientFactory, 7.1.2
- System.Net.Http.Json, 8.0.0
"
]
},
"metadata": {},
@@ -58,23 +58,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "启动WebApi项目\r\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "程序[e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n"
+ "配置文件根目录:e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
+ "启动WebApi项目\n",
+ "程序[e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\n"
]
}
],
@@ -85,6 +71,8 @@
"#r \"nuget:Microsoft.Extensions.DependencyInjection\"\n",
"#r \"nuget:Polly\"\n",
"#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
+ "#r \"nuget:Refit\" \n",
+ "#r \"nuget:Refit.HttpClientFactory\"\n",
"#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
"\n",
"global using System;\n",
@@ -121,15 +109,21 @@
"global using Polly.Registry;\n",
"global using Polly.Telemetry;\n",
"\n",
+ "global using Refit;\n",
+ "//global using Refit.HttpClientFactory;\n",
+ "\n",
"global using HttpClientStudy.Config;\n",
"global using HttpClientStudy.Core;\n",
"global using HttpClientStudy.Core.Utilities;\n",
"\n",
- "//启动已发布的WebApi项目\n",
- "Console.WriteLine(\"启动WebApi项目\");\n",
+ "//全局变量\n",
+ "var webApiBaseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
"var workDir = Environment.CurrentDirectory;\n",
"var fullPath = System.IO.Path.GetFullPath(\"./Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.exe\", workDir);\n",
- "fullPath.Display();\n",
+ "//fullPath.Display();\n",
+ "\n",
+ "//启动已发布的WebApi项目\n",
+ "Console.WriteLine(\"启动WebApi项目\");\n",
"var startMessage = AppUtility.RunWebApiExeFile(fullPath);\n",
"Console.WriteLine(startMessage);\n"
]
@@ -690,23 +684,19 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": null,
"metadata": {
+ "dotnet_interactive": {
+ "language": "csharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "csharp"
+ },
"vscode": {
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "9193\n",
- "9193\n",
- "18\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"/*\n",
" 注意:\n",
@@ -770,22 +760,19 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": null,
"metadata": {
+ "dotnet_interactive": {
+ "language": "csharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "csharp"
+ },
"vscode": {
"languageId": "polyglot-notebook"
}
},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "A: 9508\n",
- "A: 18\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"// IoC 多个HttpClient服务类\n",
"\n",
@@ -870,49 +857,209 @@
},
{
"cell_type": "code",
- "execution_count": 68,
+ "execution_count": null,
"metadata": {
+ "dotnet_interactive": {
+ "language": "csharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "csharp"
+ },
"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)"
- ]
- }
- ],
+ "outputs": [],
"source": [
- "//基础使用\n",
+ "//所有HttpClient配置\n",
"{\n",
" var services = new ServiceCollection();\n",
- " services.AddHttpClient((provider, client) => \n",
+ " //添加一个委托,用于配置所有HttpClient实例。只执行一次。\n",
+ " services.ConfigureHttpClientDefaults(builder => \n",
" {\n",
- " client.BaseAddress = new Uri(\"https://www.baidu.com\");\n",
+ " //builder.UseSocketsHttpHandler();\n",
+ " //builder.SetHandlerLifetime(TimeSpan.FromMinutes(5));\n",
+ " \n",
+ " builder.ConfigureHttpClient(hc =>\n",
+ " {\n",
+ " hc.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " });\n",
" });\n",
"\n",
+ " var factory = services.BuildServiceProvider().GetRequiredService();\n",
+ "\n",
+ " var client = factory.CreateClient();\n",
+ " var response = await client.GetAsync(\"/api/hello/ping\");\n",
+ " var data = await response.Content.ReadAsStringAsync();\n",
+ " Console.WriteLine(data);\n",
+ "}\n",
+ "\n",
+ "/*\n",
+ " 默认 HttpClient\n",
+ " 1、配置默认HttpClient时,AddHttpClient(Action)的Action不会被执行,ConfigureHttpClient()也不执行。原因暂时不明\n",
+ " 2、想配置默认HttpClient,可以使用ConfigureHttpClientDefaults方法,但此方法针对所有HttpClient实例(包括命名HttpClient)\n",
+ "*/\n",
+ "{\n",
+ " var services = new ServiceCollection();\n",
+ "\n",
+ " //默认HttpClient\n",
+ " services.AddHttpClient();\n",
+ "\n",
+ " //这种形式,不生效\n",
+ " // services.AddHttpClient((provider, client) => \n",
+ " // {\n",
+ " // client.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " // });\n",
+ "\n",
+ " //这种形式,ConfigureHttpClient 也不生效\n",
+ " // services.AddHttpClient()\n",
+ " // .ConfigureHttpClient(config=>\n",
+ " // {\n",
+ " // config.BaseAddress = new Uri(webApiBaseUrl);\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",
+ " var response = await factory.CreateClient().GetAsync(webApiBaseUrl+\"/api/hello/index\");\n",
+ " var content = await response.Content.ReadAsStringAsync();\n",
" \n",
- " Console.WriteLine(sum);\n",
+ " Console.WriteLine(content);\n",
"}\n",
"\n",
- "//\n",
+ "//命名 HttpClient\n",
"{\n",
+ " var clientA =\"httpClientA\";\n",
+ " var clientB =\"httpClientB\";\n",
"\n",
- "}"
+ " var services = new ServiceCollection();\n",
+ " services.AddHttpClient(clientA, (provider, client) => \n",
+ " {\n",
+ " client.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " });\n",
+ " services.AddHttpClient(clientB, (provider, client) => \n",
+ " {\n",
+ " client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;\n",
+ " client.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " })\n",
+ " .ConfigureHttpClient(client=>\n",
+ " {\n",
+ " client.Timeout = TimeSpan.FromSeconds(1);\n",
+ " client.DefaultRequestVersion = new Version(1, 1);\n",
+ " });\n",
+ "\n",
+ " var factory = services.BuildServiceProvider().GetRequiredService();\n",
+ "\n",
+ " var responseA = await factory.CreateClient(clientA).GetAsync(\"/api/hello/ping\");\n",
+ " var contentA = await responseA.Content.ReadAsStringAsync();\n",
+ " Console.WriteLine(contentA);\n",
+ "\n",
+ " var responseB = await factory.CreateClient(clientB).PostAsync(\"/api/hello/post\",null);\n",
+ " var contentB = await responseB.Content.ReadAsStringAsync();\n",
+ " Console.WriteLine(contentB);\n",
+ "}\n",
+ "\n",
+ "// 类型化客户端 HttpClient\n",
+ "public class HttpClientServiceA\n",
+ "{\n",
+ " public HttpClient Client { get; }\n",
+ " public HttpClientServiceA(HttpClient client)\n",
+ " {\n",
+ " Client = client;\n",
+ " Console.WriteLine(\"HttpClientServiceA => 构造函数执行一次\");\n",
+ " }\n",
+ "\n",
+ " public async Task GetIndexAsync()\n",
+ " {\n",
+ " var response = await Client.GetAsync(\"/api/hello/index\");\n",
+ " var content = await response.Content.ReadAsStringAsync();\n",
+ " return content;\n",
+ " }\n",
+ "}\n",
+ "\n",
+ "public class HttpClientServiceB\n",
+ "{\n",
+ " public HttpClient Client { get; }\n",
+ " public HttpClientServiceB(HttpClient client)\n",
+ " {\n",
+ " Client = client;\n",
+ " Console.WriteLine(\"HttpClientServiceB => 构造函数执行一次\");\n",
+ " }\n",
+ "\n",
+ " public async Task PingAsync()\n",
+ " {\n",
+ " var response = await Client.GetAsync(\"/api/hello/Ping\");\n",
+ " var content = await response.Content.ReadAsStringAsync();\n",
+ " return content;\n",
+ " }\n",
+ "}\n",
+ "\n",
+ "{\n",
+ " var services = new ServiceCollection();\n",
+ " services.AddHttpClient(client => \n",
+ " {\n",
+ " client.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " Console.WriteLine(\"HttpClientServiceA => AddHttpClient 执行一次\");\n",
+ " })\n",
+ " .ConfigureHttpClient(client=>\n",
+ " {\n",
+ " client.Timeout = TimeSpan.FromSeconds(1);\n",
+ " Console.WriteLine(\"HttpClientServiceA => ConfigureHttpClient 执行一次\");\n",
+ " });\n",
+ "\n",
+ " services.AddHttpClient(client => \n",
+ " {\n",
+ " client.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " Console.WriteLine(\"HttpClientServiceB => AddHttpClient 执行一次\");\n",
+ " })\n",
+ " .ConfigureHttpClient(client=>\n",
+ " {\n",
+ " client.Timeout = TimeSpan.FromSeconds(2);\n",
+ " Console.WriteLine(\"HttpClientServiceB => ConfigureHttpClient 执行一次\");\n",
+ " });\n",
+ "\n",
+ " var builder = services.BuildServiceProvider();\n",
+ "\n",
+ " var serviceA = builder.GetRequiredService();\n",
+ " var serviceB = builder.GetRequiredService();\n",
+ " //每获取一次类型化客户端,都会执行一交。\n",
+ " var serviceB2 = builder.GetRequiredService();\n",
+ "\n",
+ " var dataA = await serviceA.GetIndexAsync();\n",
+ " Console.WriteLine(dataA);\n",
+ "\n",
+ " var dataB = await serviceB.PingAsync();\n",
+ " Console.WriteLine(dataB);\n",
+ "\n",
+ " var dataB2 = await serviceB2.PingAsync();\n",
+ " Console.WriteLine(dataB2);\n",
+ "}\n",
+ "\n",
+ "// 类型化客户端2\n",
+ "{\n",
+ " var services = new ServiceCollection();\n",
+ " services.AddHttpClient(client => \n",
+ " {\n",
+ " client.BaseAddress = new Uri(webApiBaseUrl);\n",
+ " Console.WriteLine(\"HttpClientServiceA => AddHttpClient 执行一次\");\n",
+ " })\n",
+ " .AddTypedClient()\n",
+ " .ConfigureHttpClient(client=>\n",
+ " {\n",
+ " client.Timeout = TimeSpan.FromSeconds(1);\n",
+ " Console.WriteLine(\"HttpClientServiceA => ConfigureHttpClient 执行一次\");\n",
+ " });\n",
+ "}\n",
+ "\n",
+ "//todo: 生成式 HttpClient: Refit库\n",
+ "\n",
+ "/*\n",
+ " IFactoryHttpClient + Polly8\n",
+ " 1、引用库 Polly 和 Microsoft.Extensions.Http.Polly\n",
+ "*/\n",
+ "\n",
+ "\n"
]
},
{
diff --git a/HttpClientStudy.WebApp/Controllers/HelloController .cs b/HttpClientStudy.WebApp/Controllers/HelloController .cs
new file mode 100644
index 0000000..594e0b3
--- /dev/null
+++ b/HttpClientStudy.WebApp/Controllers/HelloController .cs
@@ -0,0 +1,63 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace HttpClientStudy.WebApp.Controllers
+{
+ ///
+ /// 简单接口 控制器
+ ///
+ [Route("api/[controller]/[action]")]
+ [ApiController]
+ public class HelloController : ControllerBase
+ {
+ private ILogger _logger;
+
+ ///
+ /// 构造
+ ///
+ public HelloController (ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ ///
+ /// Ping
+ ///
+ /// Pong
+ [HttpGet]
+ public IActionResult Ping()
+ {
+ return Ok("Pong");
+ }
+
+ ///
+ /// Index
+ ///
+ ///
+ [HttpGet]
+ public IActionResult Index()
+ {
+ return Ok("Index");
+ }
+
+ ///
+ /// Get请求
+ ///
+ ///
+ [HttpGet]
+ public IActionResult Get()
+ {
+ return Ok("Hello, world!");
+ }
+
+ ///
+ /// Post 请求
+ ///
+ ///
+ [HttpPost]
+ public IActionResult Post()
+ {
+ return Ok("Hello, world!");
+ }
+ }
+}