客户端管理完成

main
bicijinlian 8 months ago
parent 5467df9657
commit 4075420fb6

@ -48,6 +48,7 @@
"source": [ "source": [
"//全局设置,行运行一次,为后续准备\n", "//全局设置,行运行一次,为后续准备\n",
"#r \"nuget:System.Net.Http.Json\"\n", "#r \"nuget:System.Net.Http.Json\"\n",
"#r \"nuget:Microsoft.Net.Http.Headers\"\n",
"#r \"nuget:Microsoft.Extensions.Http\"\n", "#r \"nuget:Microsoft.Extensions.Http\"\n",
"#r \"nuget:Microsoft.Extensions.DependencyInjection\"\n", "#r \"nuget:Microsoft.Extensions.DependencyInjection\"\n",
"#r \"nuget:Microsoft.Extensions.Logging\" \n", "#r \"nuget:Microsoft.Extensions.Logging\" \n",
@ -1999,6 +2000,12 @@
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": { "metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": { "vscode": {
"languageId": "polyglot-notebook" "languageId": "polyglot-notebook"
} }
@ -2048,6 +2055,12 @@
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": { "metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": { "vscode": {
"languageId": "polyglot-notebook" "languageId": "polyglot-notebook"
} }
@ -2097,6 +2110,320 @@
"source": [ "source": [
"## 8、综合管理工厂 + 类型化客户端 请求管道 + Polly(默认使用 连接池和IoC容器)" "## 8、综合管理工厂 + 类型化客户端 请求管道 + Polly(默认使用 连接池和IoC容器)"
] ]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 综合示例1"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"/* 综合示例1\n",
" 工厂 + 类型化客户端 + 管道 + Polly + 日志(自定义) \n",
"*/\n",
"\n",
"//类型化客户端\n",
"public class HelloApiService \n",
"{\n",
" public HttpClient Client { get; set; }\n",
"\n",
" public HelloApiService(HttpClient httpClient)\n",
" {\n",
" Client = httpClient;\n",
" }\n",
"\n",
" public async Task<string> Ping()\n",
" {\n",
" var content = await Client.GetStringAsync(\"/api/Hello/Ping\");\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> Index()\n",
" {\n",
" var content = await Client.GetStringAsync(\"/api/Hello/Index\");\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> Get()\n",
" {\n",
" var content = await Client.GetStringAsync(\"/api/Hello/Get\");\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> Post()\n",
" {\n",
" var response = await Client.PostAsync(\"/api/Hello/Post\", null);\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" return content;\n",
" }\n",
"}\n",
"\n",
"//类开型客户端\n",
"public class Polly8ApiService \n",
"{\n",
"\n",
" public HttpClient Client { get; set; }\n",
"\n",
" public Polly8ApiService(HttpClient httpClient)\n",
" {\n",
" Client = httpClient;\n",
" } \n",
"\n",
" public async Task<string> Hello()\n",
" {\n",
" var content = await Client.GetStringAsync(\"/api/Polly8/Hello\");\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> Exception()\n",
" {\n",
" var response = await Client.GetAsync(\"/api/Polly8/Exception\");\n",
" response.EnsureSuccessStatusCode();\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> RetryException()\n",
" {\n",
" var response = await Client.GetAsync(\"/api/Polly8/RetryException\");\n",
" response.EnsureSuccessStatusCode();\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> RandomException()\n",
" {\n",
" var response = await Client.GetAsync(\"/api/Polly8/RandomException\");\n",
" response.EnsureSuccessStatusCode();\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" return content;\n",
" }\n",
"\n",
" public async Task<string> ToggleException()\n",
" {\n",
" var response = await Client.GetAsync(\"/api/Polly8/ToggleException?toggleId=\"+Guid.NewGuid().ToString());\n",
" response.EnsureSuccessStatusCode();\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" return content;\n",
" }\n",
"}\n",
"\n",
"//Token管理中间件\n",
"public class TokenDelegatingHandler : DelegatingHandler \n",
"{\n",
" protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)\n",
" {\n",
" Console.WriteLine(\"TokenDelegatingHandler -> Send -> Added Token\");\n",
"\n",
" if (!request.Headers.Contains(Microsoft.Net.Http.Headers.HeaderNames.Authorization)) \n",
" {\n",
" Console.WriteLine(\"没有 Token, TokenDelegatingHandler 添加之\");\n",
" request.Headers.Add(Microsoft.Net.Http.Headers.HeaderNames.Authorization, \"Bearer \" + \"a.b.c\");\n",
" }\n",
" else\n",
" {\n",
" Console.WriteLine($\"已有Token, {request.Headers.Authorization}\");\n",
" }\n",
"\n",
" HttpResponseMessage response = base.Send(request, cancellationToken);\n",
"\n",
" Console.WriteLine(\"TokenDelegatingHandler -> Send -> After\");\n",
"\n",
" return response;\n",
" }\n",
"\n",
" protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n",
" {\n",
" Console.WriteLine(\"TokenDelegatingHandler -> SendAsync -> Before\");\n",
"\n",
" HttpResponseMessage response = await base.SendAsync(request, cancellationToken);\n",
"\n",
" Console.WriteLine(\"TokenDelegatingHandler -> SendAsync -> After\");\n",
"\n",
" return response;\n",
" }\n",
"}\n",
"\n",
"//自定义日志\n",
"public class CustomLogger : IHttpClientLogger\n",
"{\n",
" public object? LogRequestStart(HttpRequestMessage request)\n",
" {\n",
" return null;\n",
" }\n",
"\n",
" public void LogRequestStop(object? ctx, HttpRequestMessage request, HttpResponseMessage response, TimeSpan elapsed)\n",
" {\n",
" Console.WriteLine($\"自定义日志:{request.Method} {request.RequestUri?.AbsoluteUri} - {(int)response.StatusCode} {response.StatusCode} in {elapsed.TotalMilliseconds}ms\");\n",
" }\n",
"\n",
" public void LogRequestFailed(object? ctx, HttpRequestMessage request, HttpResponseMessage? response, Exception e, TimeSpan elapsed)\n",
" {\n",
" Console.WriteLine($\"自定义日志:{request.Method} {request.RequestUri?.AbsoluteUri} - Exception {e.GetType().FullName}: {e.Message}\");\n",
" }\n",
"}\n",
"\n",
"//polly策略\n",
"var policy = Policy\n",
" .Handle<HttpRequestException>()\n",
" .OrResult<HttpResponseMessage>(message => message.StatusCode != System.Net.HttpStatusCode.OK)\n",
" .WaitAndRetryAsync(new TimeSpan[]{TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2),TimeSpan.FromSeconds(4),});\n",
"\n",
"//使用\n",
"{\n",
" var services = new ServiceCollection();\n",
"\n",
" //注册基础类型\n",
" services\n",
" //注册日志类\n",
" .AddTransient<CustomLogger>()\n",
" .AddScoped<TokenDelegatingHandler>()\n",
" ;\n",
"\n",
" //基础配置\n",
" services\n",
" // 基础日志配置(默认日志)\n",
" .AddLogging(builder => \n",
" {\n",
" //日志级别\n",
" builder.SetMinimumLevel(LogLevel.Trace);\n",
"\n",
" //控制台日志\n",
" builder.AddConsole();\n",
" })\n",
" //全局配置\n",
" .ConfigureHttpClientDefaults(clientBuilder =>\n",
" {\n",
" clientBuilder.AddDefaultLogger();\n",
" clientBuilder.ConfigureHttpClient(client => \n",
" {\n",
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
" });\n",
" });\n",
"\n",
" //默认命名客户端\n",
" services.AddHttpClient<HttpClient>(string.Empty, config => \n",
" {\n",
" config.DefaultRequestHeaders.Add(\"X-Custom-Demo\", \"true\");\n",
" })\n",
" //配置客户端\n",
" .ConfigureHttpClient(client => \n",
" {\n",
" //client.BaseAddress = new Uri(webApiBaseUrl);\n",
" client.Timeout = TimeSpan.FromSeconds(10);\n",
" })\n",
" //添加类型化客户端\n",
" .AddTypedClient<HelloApiService>()\n",
" //添加自定义管道\n",
" .AddHttpMessageHandler<TokenDelegatingHandler>()\n",
" //添加默认日志:全局配置已添加\n",
" //.AddDefaultLogger()\n",
" //添加自定义日志\n",
" .AddLogger<CustomLogger>()\n",
" //日志转发头(所有请求头)\n",
" .RedactLoggedHeaders( headerName => true)\n",
"\n",
" //配置SocketsHttpHandler\n",
" .UseSocketsHttpHandler(config =>\n",
" {\n",
" //配置连接池等\n",
" config.Configure((handler,provider) => \n",
" {\n",
" handler.AllowAutoRedirect = true;\n",
" handler.PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30);\n",
" handler.PooledConnectionLifetime = TimeSpan.FromSeconds(30);\n",
" handler.UseProxy = false;\n",
" handler.UseCookies = true;\n",
" });\n",
" })\n",
" //设置生命周期\n",
" .SetHandlerLifetime(TimeSpan.FromSeconds(30))\n",
" //Polly策略配置\n",
" .AddPolicyHandler(policy)\n",
" //便捷配置\n",
" .AddTransientHttpErrorPolicy(builder => builder.CircuitBreakerAsync<HttpResponseMessage>(11, TimeSpan.FromSeconds(30)))\n",
" ;\n",
"\n",
" //自定义\n",
" services.AddHttpClient<HttpClient>(\"ClientA\", config => \n",
" {\n",
" config.DefaultRequestHeaders.Add(\"X-Custom-Demo\", \"ClientA\");\n",
" })\n",
" //配置客户端\n",
" .ConfigureHttpClient(client => \n",
" {\n",
" //client.BaseAddress = new Uri(webApiBaseUrl);\n",
" client.Timeout = TimeSpan.FromSeconds(10);\n",
" })\n",
" //添加类型化客户端\n",
" .AddTypedClient<Polly8ApiService>()\n",
" //添加自定义管道\n",
" .AddHttpMessageHandler<TokenDelegatingHandler>()\n",
" //添加默认日志:全局配置已添加\n",
" //.AddDefaultLogger()\n",
" //添加自定义日志\n",
" .AddLogger<CustomLogger>()\n",
" //日志转发头(所有请求头)\n",
" .RedactLoggedHeaders( headerName => true)\n",
" //配置SocketsHttpHandler\n",
" .UseSocketsHttpHandler(config =>\n",
" {\n",
" //配置连接池等\n",
" config.Configure((handler,provider) => \n",
" {\n",
" handler.AllowAutoRedirect = true;\n",
" handler.PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30);\n",
" handler.PooledConnectionLifetime = TimeSpan.FromSeconds(30);\n",
" handler.UseProxy = false;\n",
" handler.UseCookies = true;\n",
" });\n",
" })\n",
" //设置生命周期\n",
" .SetHandlerLifetime(TimeSpan.FromSeconds(30))\n",
" //Polly策略配置\n",
" .AddPolicyHandler(policy)\n",
" //便捷配置\n",
" .AddTransientHttpErrorPolicy(builder => builder.CircuitBreakerAsync<HttpResponseMessage>(11, TimeSpan.FromSeconds(30)))\n",
" ;\n",
"\n",
" var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();\n",
"\n",
" var defaultClient = factory.CreateClient();\n",
" var defaultContent = await defaultClient.GetStringAsync(\"api/hello/ping\");\n",
" Console.WriteLine(defaultContent);\n",
"\n",
" var clientA = factory.CreateClient();\n",
" var contentA = await clientA.GetStringAsync(\"api/polly8/hello\");\n",
" Console.WriteLine(contentA);\n",
"\n",
" //类型化客户端\n",
" HelloApiService helloApiService = services.BuildServiceProvider().GetRequiredService<HelloApiService>();\n",
" Console.WriteLine(await helloApiService.Ping());\n",
" Console.WriteLine(await helloApiService.Index());\n",
" Console.WriteLine(await helloApiService.Get());\n",
" Console.WriteLine(await helloApiService.Post());\n",
"\n",
" Polly8ApiService polly8ApiService = services.BuildServiceProvider().GetRequiredService<Polly8ApiService>();\n",
" Console.WriteLine(await polly8ApiService.Hello());\n",
"\n",
"}\n"
]
} }
], ],
"metadata": { "metadata": {

@ -0,0 +1,23 @@
namespace HttpClientStudy.Core.CustomHttpClient
{
/// <summary>
/// 自定义日志
/// </summary>
public class CustomLogger : IHttpClientLogger
{
public object? LogRequestStart(HttpRequestMessage request)
{
return null;
}
public void LogRequestStop(object? ctx, HttpRequestMessage request, HttpResponseMessage response, TimeSpan elapsed)
{
Console.WriteLine($"自定义日志:{request.Method} {request.RequestUri?.AbsoluteUri} - {(int)response.StatusCode} {response.StatusCode} in {elapsed.TotalMilliseconds}ms");
}
public void LogRequestFailed(object? ctx, HttpRequestMessage request, HttpResponseMessage? response, Exception e, TimeSpan elapsed)
{
Console.WriteLine($"自定义日志:{request.Method} {request.RequestUri?.AbsoluteUri} - Exception {e.GetType().FullName}: {e.Message}");
}
}
}

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HttpClientStudy.Core.CustomHttpClient
{
/// <summary>
/// HelloApi
/// 类型化客户端
/// </summary>
public class HelloApiService
{
public HttpClient Client { get; set; }
public HelloApiService(HttpClient httpClient)
{
Client = httpClient;
}
public async Task<string> Ping()
{
var content = await Client.GetStringAsync("/api/Hello/Ping");
return content;
}
public async Task<string> Index()
{
var content = await Client.GetStringAsync("/api/Hello/Index");
return content;
}
public async Task<string> Get()
{
var content = await Client.GetStringAsync("/api/Hello/Get");
return content;
}
public async Task<string> Post()
{
var response = await Client.PostAsync("/api/Hello/Post", null);
var content = await response.Content.ReadAsStringAsync();
return content;
}
}
}

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HttpClientStudy.Core.CustomHttpClient
{
/// <summary>
/// Polly V8
/// 类型化客户端
/// </summary>
public class Polly8ApiService
{
public HttpClient Client { get; set; }
public Polly8ApiService(HttpClient httpClient)
{
Client = httpClient;
}
public async Task<string> Hello()
{
var content = await Client.GetStringAsync("/api/Polly8/Hello");
return content;
}
public async Task<string> Exception()
{
var response = await Client.GetAsync("/api/Polly8/Exception");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content;
}
public async Task<string> RetryException()
{
var response = await Client.GetAsync("/api/Polly8/RetryException");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content;
}
public async Task<string> RandomException()
{
var response = await Client.GetAsync("/api/Polly8/RandomException");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content;
}
public async Task<string> ToggleException()
{
var response = await Client.GetAsync("/api/Polly8/ToggleException?toggleId=" + Guid.NewGuid().ToString());
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content;
}
}
}

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;
namespace HttpClientStudy.Core.CustomHttpClient
{
/// <summary>
/// oken管理中间件
/// </summary>
public class TokenDelegatingHandler : DelegatingHandler
{
protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("TokenDelegatingHandler -> Send -> Added Token");
if (request.Headers.Authorization is null)
{
request.Headers.Add(HeaderNames.Accept, "Bearer " + "a.b.c");
}
HttpResponseMessage response = base.Send(request, cancellationToken);
Console.WriteLine("TokenDelegatingHandler -> Send -> After");
return response;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("TokenDelegatingHandler -> SendAsync -> Before");
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("TokenDelegatingHandler -> SendAsync -> After");
return response;
}
}
}

@ -3,14 +3,46 @@ global using System.Linq;
global using System.Text; global using System.Text;
global using System.Threading; global using System.Threading;
global using System.Threading.Tasks; global using System.Threading.Tasks;
global using System.Collections.Generic;
global using System.Net;
global using System.Net.Mime; global using System.Net.Mime;
global using System.Net.Http; global using System.Net.Http;
global using System.Net.Http.Json; global using System.Net.Http.Json;
global using System.Net.Http.Headers; global using System.Net.Http.Headers;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Configuration.Memory;
global using Microsoft.Extensions.Configuration.CommandLine;
global using Microsoft.Extensions.Configuration.EnvironmentVariables;
global using Microsoft.Extensions.Configuration.UserSecrets;
global using Microsoft.Extensions.Configuration.KeyPerFile;
global using Microsoft.Extensions.Configuration.Ini;
global using Microsoft.Extensions.Configuration.Json;
global using Microsoft.Extensions.Configuration.Xml;
global using Microsoft.Extensions.Options;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.Net.Http;
global using Microsoft.Net.Http.Headers;
global using Microsoft.Extensions.Http;
global using Microsoft.Extensions.Http.Logging;
global using Polly;
global using Polly.NoOp;
global using Polly.Retry;
global using Polly.Bulkhead;
global using Polly.Fallback;
global using Polly.Hedging;
global using Polly.RateLimit;
global using Polly.Caching;
global using Polly.Timeout;
global using Polly.CircuitBreaker;
global using Polly.Wrap;
global using Polly.Registry;
global using Polly.Utilities;
global using Polly.Telemetry;
global using Polly.Extensions;
global using Polly.Extensions.Http;
global using HttpClientStudy.Config; global using HttpClientStudy.Config;
global using HttpClientStudy.Model; global using HttpClientStudy.Model;

Loading…
Cancel
Save