main
bicijinlian 2 months ago
parent 33c43b039e
commit 9163c156cb

@ -55,15 +55,17 @@ Start-Process -FilePath "Publish\HttpClientStudy.WebApp\HttpClientStudy.WebApp.e
#!pwsh
# 关闭项目进程
$WebAppProcName ="HttpClientStudy.WebApp";
$WebAppProc = Get-Process $WebAppProcName -ErrorAction Ignore
if($null -eq $WebAppProc)
{
$WebAppProcName ="HttpClientStudy.WebApp";
$WebAppProc = Get-Process $WebAppProcName -ErrorAction Ignore
if($null -eq $WebAppProc)
{
Write-Host "进程没有找到,可能已经退出"
}
else {
}
else {
$WebAppProc.Kill();
Write-Host "$WebAppProcName 进程已退出"
}
}
#!markdown
@ -72,15 +74,12 @@ else {
#!csharp
#r "nuget:Microsoft.Extensions.DependencyInjection,8.0.1"
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
//查看各种程序路径
{

@ -2174,7 +2174,7 @@
" }\n",
"}\n",
"\n",
"//类型客户端\n",
"//类型客户端\n",
"public class Polly8ApiService \n",
"{\n",
"\n",

@ -11,7 +11,7 @@
}
},
"source": [
"# HttpClient 使用原则"
"# HttpClient 处理响应数据"
]
},
{
@ -33,7 +33,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
@ -45,15 +45,22 @@
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"配置文件根目录c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
"启动WebApi项目\n",
"程序[c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\n"
]
}
],
"source": [
"//初始化,只执行一次\n",
"\n",
"// 引用nuget包和类库文件\n",
"//#r \"nuget:Microsoft.Net.Http.Headers,8.0.12\"\n",
"\n",
"#r \"./Publish/HttpClientStudy.Model/HttpClientStudy.Model.dll\"\n",
"//已引用相关类库\n",
"#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
"\n",
"//全局引用\n",
@ -67,6 +74,9 @@
"global using System.Net.Mime;\n",
"global using System.Net.Http.Json;\n",
"\n",
"global using Microsoft.Extensions.DependencyInjection;\n",
"global using Microsoft.Extensions.DependencyInjection.Extensions;\n",
"\n",
"global using HttpClientStudy.Config;\n",
"global using HttpClientStudy.Model;\n",
"global using HttpClientStudy.Core;\n",
@ -98,6 +108,103 @@
"## 2、处理响应状态"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"响应码正常:{\"data\":{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\n",
"响应码异常:状态码 BadRequest\n",
"响应正常:内容为 HttpClientStudy.Model.BaseResult`1[HttpClientStudy.Model.Account]\n",
"请求异常Response status code does not indicate success: 400 (Bad Request).\n",
"请求异常Response status code does not indicate success: 400 (Bad Request).\n"
]
}
],
"source": [
"//判断响应码:正常\n",
"{\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=1\");\n",
" if(response.StatusCode == System.Net.HttpStatusCode.OK)\n",
" {\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" Console.WriteLine($\"响应码正常:{content}\");\n",
" }\n",
"}\n",
"\n",
"//判断响应码:非正常\n",
"{\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=b\");\n",
" if(response.StatusCode != System.Net.HttpStatusCode.OK)\n",
" {\n",
" Console.WriteLine($\"响应码异常:状态码 {response.StatusCode}\");\n",
" }\n",
"}\n",
"\n",
"//确保正确响应:正常\n",
"{\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=1\");\n",
"\n",
" //确保异常\n",
" response.EnsureSuccessStatusCode();\n",
"\n",
" var result = await response.Content.ReadFromJsonAsync<BaseResult<Account>>();\n",
" //result.Display();\n",
" Console.WriteLine($\"响应正常:内容为 {result}\");\n",
"}\n",
"\n",
"//确保正确响应:异常\n",
"{\n",
" try \n",
" {\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=c\");\n",
" \n",
" //确保异常\n",
" response.EnsureSuccessStatusCode();\n",
" //result.Display();\n",
"\n",
" var result = await response.Content.ReadFromJsonAsync<BaseResult<Account>>();\n",
" Console.WriteLine($\"响应正常:内容为 {result}\");\n",
" }\n",
" catch(Exception e)\n",
" {\n",
" Console.WriteLine($\"请求异常:{e.Message}\");\n",
" } \n",
"}\n",
"\n",
"//使用 ry catch 捕获所有异常\n",
"{\n",
" try \n",
" {\n",
" var result = await SharedClient.GetFromJsonAsync<BaseResult<Account>>(\"api/Normal/GetAccount?id=a\");\n",
" //result.Display();\n",
" Console.WriteLine($\"响应正常:内容为 {result}\");\n",
" }\n",
" catch(Exception e)\n",
" {\n",
" Console.WriteLine($\"请求异常:{e.Message}\");\n",
" }\n",
" finally\n",
" {\n",
" //收发业务\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -105,6 +212,305 @@
"## 3、处理异常响应"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.1 try catch"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"接口异常Response status code does not indicate success: 400 (Bad Request).\r\n"
]
}
],
"source": [
"//try catch 常规异常处理\n",
"{\n",
" try \n",
" {\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=c\");\n",
" \n",
" //确保异常\n",
" response.EnsureSuccessStatusCode();\n",
"\n",
" var result = await response.Content.ReadFromJsonAsync<BaseResult<Account>>();\n",
" Console.WriteLine($\"响应正常:内容为 {result}\");\n",
" }\n",
" catch(Exception e)\n",
" {\n",
" Console.WriteLine($\"接口异常:{e.Message}\");\n",
" }\n",
" finally\n",
" {\n",
" //清理\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {
"vscode": {
"languageId": "polyglot-notebook"
}
},
"source": [
"### 3.2 管道统一处理"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ExceptionDelegatingHandler -> SendAsync -> Before\n",
"中间件中接口调用异常Response status code does not indicate success: 400 (Bad Request).\n",
"ExceptionDelegatingHandler -> Send -> After\n",
"接口异常:状态码 BadRequest\n"
]
}
],
"source": [
"//异常处理管理中间件\n",
"public class ExceptionDelegatingHandler : DelegatingHandler \n",
"{\n",
" protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)\n",
" {\n",
" Console.WriteLine(\"ExceptionDelegatingHandler -> Send -> Added Token\");\n",
"\n",
" HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);\n",
" try \n",
" {\n",
" response = base.Send(request, cancellationToken);\n",
" response.EnsureSuccessStatusCode();\n",
" }\n",
" catch(Exception ex)\n",
" {\n",
" //统一异常处理,当然也可以分类别处理\n",
" Console.WriteLine($\"中间件中,接口调用异常:{ex.Message}\");\n",
" }\n",
" finally\n",
" {\n",
" Console.WriteLine(\"ExceptionDelegatingHandler -> Send -> After\");\n",
" }\n",
"\n",
" return response;\n",
" }\n",
"\n",
" protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n",
" {\n",
" Console.WriteLine(\"ExceptionDelegatingHandler -> SendAsync -> Before\");\n",
"\n",
" HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);\n",
" try \n",
" {\n",
" response = await base.SendAsync(request, cancellationToken);\n",
" //可以根据状态码,分别进行处理\n",
" response.EnsureSuccessStatusCode();\n",
" }\n",
" catch(Exception ex)\n",
" {\n",
" //统一异常处理,当然也可以分类别处理\n",
" //可以重试等操作\n",
" Console.WriteLine($\"中间件中,接口调用异常:{ex.Message}\");\n",
" }\n",
" finally\n",
" {\n",
" Console.WriteLine(\"ExceptionDelegatingHandler -> Send -> After\");\n",
" }\n",
"\n",
" return response;\n",
" }\n",
"}\n",
"\n",
"//使用异常管道,发送请求\n",
"{\n",
" //使用管道中间件,统一处理\n",
" ExceptionDelegatingHandler exceptionHandler = new ExceptionDelegatingHandler()\n",
" {\n",
" InnerHandler = new SocketsHttpHandler()\n",
" };\n",
"\n",
" HttpClient clientWithExceptionHandler = new HttpClient(exceptionHandler)\n",
" {\n",
" BaseAddress = new Uri(webApiBaseUrl),\n",
" };\n",
"\n",
" //发送请求\n",
" var response = await clientWithExceptionHandler.GetAsync(\"api/Normal/GetAccount?id=c\");\n",
" if(response.StatusCode == System.Net.HttpStatusCode.OK)\n",
" {\n",
" var result = await response.Content.ReadFromJsonAsync<BaseResult<Account>>();\n",
" Console.WriteLine($\"响应正常:内容为 {result}\");\n",
" }\n",
" else\n",
" {\n",
" Console.WriteLine($\"接口异常:状态码 {response.StatusCode}\");\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.3 类型化客户端统一处理"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"远程调用异常Response status code does not indicate success: 404 (Not Found).\r\n"
]
}
],
"source": [
"//类型化客户端\n",
"public class HelloApiService \n",
"{\n",
" public HttpClient Client { get; set; }\n",
"\n",
" public HelloApiService(HttpClient httpClient)\n",
" {\n",
" Client = httpClient;\n",
" }\n",
"\n",
" //处理异常也可以结合AOP进行统一拦截处理\n",
" public async Task<string> Ping()\n",
" {\n",
" try \n",
" {\n",
" var content = await Client.GetStringAsync(\"/api/Hello/Ping2\");\n",
" return content;\n",
" }\n",
" catch(Exception ex)\n",
" {\n",
" return $\"远程调用异常:{ex.Message}\";\n",
" }\n",
" }\n",
"}\n",
"\n",
"//使用\n",
"{\n",
" //注册类型化客户端\n",
" var services = new ServiceCollection();\n",
" services.AddHttpClient<HelloApiService>(client => \n",
" {\n",
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
" })\n",
" .ConfigureHttpClient(client=>\n",
" {\n",
" client.Timeout = TimeSpan.FromSeconds(1);\n",
" });\n",
" \n",
" //使用类型化客户端,进行远程调用\n",
" var apiService = services.BuildServiceProvider().GetService<HelloApiService>();\n",
" var s = await apiService.Ping();\n",
" Console.WriteLine(s);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.4 Polly库(重试、降级、熔断等,可结合类型化客户端和工厂模式)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"//Polly进行异常处理\n",
"#r \"nuget:Polly,8.5.1\"\n",
"#r \"nuget:Microsoft.Extensions.Http.Polly,8.0.12\"\n",
"using Polly;\n",
"var services = new ServiceCollection();\n",
"services.AddHttpClient(string.Empty)\n",
" //配置默认命名客户端\n",
" .ConfigureHttpClient(client => \n",
" {\n",
" client.BaseAddress = new Uri(webApiBaseUrl);\n",
" })\n",
" //设置Policy错误处理快捷扩展方法\n",
" .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync\n",
" (\n",
" new[]\n",
" {\n",
" TimeSpan.FromSeconds(1),\n",
" TimeSpan.FromSeconds(2),\n",
" TimeSpan.FromSeconds(4),\n",
" }\n",
" ))\n",
" //可以多次调用:设置多个策略\n",
" .AddTransientHttpErrorPolicy(builder => builder.RetryAsync(1));\n",
"\n",
"var factory = services.BuildServiceProvider().GetService<IHttpClientFactory>();\n",
"var content = await factory.CreateClient().GetStringAsync(\"/api/polly8/RandomException\");\n",
"Console.WriteLine($\"响应内容:{content}\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -119,12 +525,86 @@
"### 4.1 接收响应头数据"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"响应头:\n",
"Date = Fri, 17 Jan 2025 02:32:12 GMT\n",
"Server = Kestrel\n",
"Transfer-Encoding = chunked\n",
"X-WebApi-UseTime = 0\n"
]
}
],
"source": [
"//响应头信息\n",
"{\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=1\");\n",
"\n",
" Console.WriteLine(\"响应头:\");\n",
" foreach(var header in response.Headers)\n",
" {\n",
" var headerValues = string.Join(\",\", header.Value);\n",
" Console.WriteLine($\"{header.Key} = {headerValues}\");\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4.2 接收响应体数据"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"响应体数据:{\"data\":{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\r\n"
]
}
],
"source": [
"//响应体数据(json为例)\n",
"{\n",
" var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=1\");\n",
" //获取响应体内容\n",
" var content = await response.Content.ReadAsStringAsync();\n",
" Console.WriteLine($\"响应体数据:{content}\"); \n",
"}"
]
}
],
"metadata": {
@ -134,7 +614,11 @@
"name": ".net-csharp"
},
"language_info": {
"name": "python"
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "12.0"
},
"polyglot_notebook": {
"kernelInfo": {

Loading…
Cancel
Save