You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HttpClientStudy/Docs/1.3.3.基础使用.处理响应.ipynb

628 lines
18 KiB
Plaintext

11 months ago
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
2 months ago
"# HttpClient 处理响应数据"
11 months ago
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"source": [
4 months ago
"## 1、初始化及全局设置"
]
},
{
"cell_type": "code",
"execution_count": 1,
4 months ago
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
2 months ago
"outputs": [
{
"data": {
"text/markdown": [
"## 初始化\n",
"这是全局共用文件包括Nuget包引用、全局类库引用、全局文件引用、全局命名空间引用、全局变量、全局方法、全局类定义等功能。\n",
"\n",
"在业务笔记中引用,执行其它单元格之前先执行一次。"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.DependencyInjection, 9.0.3</span></li><li><span>Microsoft.Extensions.Http, 9.0.3</span></li><li><span>Microsoft.Extensions.Http.Polly, 9.0.3</span></li><li><span>Microsoft.Extensions.Logging, 9.0.3</span></li><li><span>Microsoft.Extensions.Logging.Console, 9.0.3</span></li><li><span>Microsoft.Net.Http.Headers, 9.0.3</span></li><li><span>Polly, 8.5.2</span></li><li><span>Refit, 8.0.0</span></li><li><span>Refit.HttpClientFactory, 8.0.0</span></li><li><span>System.Net.Http.Json, 9.0.3</span></li></ul></div></div>"
]
},
"metadata": {},
"output_type": "display_data"
},
2 months ago
{
"name": "stdout",
"output_type": "stream",
"text": [
"配置文件根目录d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
"配置文件根目录d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
"启动WebApi项目...\n",
"程序[d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\n",
"已启动WebApi项目,保持窗口打开状态!\n",
"初始化完成!\n"
2 months ago
]
}
],
4 months ago
"source": [
"//初始化:必须先执行一次\n",
"#!import ./ini.ipynb"
4 months ago
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2、处理响应状态"
]
},
2 months ago
{
"cell_type": "code",
"execution_count": 2,
2 months ago
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"配置文件根目录d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
2 months ago
"响应码正常:{\"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",
"}"
]
},
4 months ago
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3、处理异常响应"
]
},
2 months ago
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.1 try catch"
]
},
{
"cell_type": "code",
"execution_count": 3,
2 months ago
"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": 4,
2 months ago
"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": 5,
2 months ago
"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": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"响应内容28\r\n"
]
}
],
2 months ago
"source": [
"//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}\");"
]
},
4 months ago
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4、处理响应数据"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4.1 接收响应头数据"
]
},
2 months ago
{
"cell_type": "code",
"execution_count": 12,
2 months ago
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"响应头:\n",
"Date = Fri, 04 Apr 2025 08:13:51 GMT\n",
2 months ago
"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",
"}"
]
},
4 months ago
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4.2 接收响应体数据"
11 months ago
]
2 months ago
},
{
"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",
"}"
]
11 months ago
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"language_info": {
2 months ago
"file_extension": ".cs",
"mimetype": "text/x-csharp",
"name": "C#",
"pygments_lexer": "csharp",
"version": "12.0"
11 months ago
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}