diff --git a/Docs/1.1.概述.ipynb b/Docs/1.1.概述.ipynb index 021698a..c44813e 100644 --- a/Docs/1.1.概述.ipynb +++ b/Docs/1.1.概述.ipynb @@ -37,6 +37,15 @@ "![图](./Assets/概要图.svg) " ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "HttpClient是应用程序进程中,封装调用远程请求的类库。整个远程的执行,是由进程向操作系统申请,经由网卡、路由器等网络设备转换、传输到一台Web服务器,然后返回的过程, HttpClient只是其中一个很小的环节。\n", + "\n", + "在这个基础上,理解初代HttpClient问题(网络套接字用完 DNS问题等)、使用原则、请求管道、工厂模式、类型化客户端、Polly等项时会很轻松。比如:即便使用Using包括了 HttpClient,也会有套接字耗尽的问题,是因为using只能释放进程中的对象,但进程管理不了系统及网卡(这是在进程之上的),更不用说路由和互联网及被调用方的服务器了。" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -44,6 +53,28 @@ "## 前世今生" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### WebRequest\n", + "这是.NET创建者最初开发用于使用HTTP请求的标准类。\n", + "\n", + "使用HttpWebRequest可以让开发者控制请求/响应流程的各个方面,如 timeouts, cookies, headers, protocols。另一个好处是HttpWebRequest类不会阻塞UI线程。例如,当您从响应很慢的API服务器下载大文件时,您的应用程序的UI不会停止响应。\n", + "\n", + "HttpWebRequest通常和WebResponse一起使用,一个发送请求,一个获取数据。HttpWebRquest更为底层一些,能够对整个访问过程有个直观的认识,但同时也更加复杂一些。\n", + "\n", + "### WebClient\n", + "WebClient是一种更高级别的抽象,是HttpWebRequest为了简化最常见任务而创建的,使用过程中你会发现他缺少基本的header,timeoust的设置,不过这些可以通过继承httpwebrequest来实现。使用WebClient可能比HttpWebRequest直接使用更慢(大约几毫秒)。但这种“低效率”带来了巨大的好处:它需要更少的代码和隐藏了细节处理,更容易使用,并且在使用它时你不太可能犯错误。\n", + "\n", + "### HttpClient\n", + "HttpClient提供强大的功能,并提供了异步支持,可以轻松配合async await 实现异步请求等。\n", + "\n", + "随着更新发展,已经解决了前期问题(套接字耗尽、DNS、不灵活等),并发展了完整、完善的体系(基于Task的异步、连接池、类型化的客户端、工厂模式、Polly等)。\n", + "\n", + "是本项目的学习目标。" + ] + }, { "cell_type": "markdown", "metadata": { @@ -58,14 +89,50 @@ } }, "source": [ - "## 架构图" + "## HttpClient架构图\n", + "\n", + "### .NetFramework 4及4.5 架构\n", + "HttpClient最初是作为NuGet包提供的,该包可以选择包含在.NET Framework 4.0项目中。在.NET Framework 4.5中,它作为BCL(基本类库)的一部分在框中提供。它建立在预先存在的HttpWebRequest实现之上。在.NET Framework中,ServicePoint API可用于控制和管理HTTP连接,包括通过为端点配置ConnectionLeaseTimeout来设置连接寿命。\n", + "\n", + "![.NetFramework](./Assets/架构.001.png)\n", + "\n", + "### .NET Core 1.0及1.1 架构\n", + ".NET Core 1.0最初于2016年6月发布。与.NET Framework中可用的版本相比,此第一个版本的API接口要小得多,主要用于构建ASP.NET Core Web应用程序。由于.NET Core 1.0是HttpClient,因此提供了API。但是,不包括用于HttpWebRequest和ServicePoint的API。.NET Core 1.0中的HttpClient直接建立在使用非托管代码的OS平台API之上,Windows API使用WinHTTP,Linux和Mac使用LibCurl。\n", + "\n", + "值得注意的是:到2016年8月,很快就注意到,重新使用HttpClient实例以防止套接字耗尽的建议有一个相当麻烦的副作用:DNS\n", + "\n", + "![.NetFramework](./Assets/架构.002.png)\n", + "\n", + "### .NET Core 2.0 架构\n", + "在.NET Core 2.0中,添加了HttpWebRequest以支持.NET Standard 2.0。它位于HttpClient实现的顶层,这与.NET Framework 4.5+中的工作原理相反。还添加了ServicePoint,尽管它的许多API接口要么要么会抛出未实现的异常,要么根本就没有实现。\n", + "\n", + "![.NetFramework](./Assets/架构.003.png)\n", + "\n", + "### .NET CORE 2.1及后续 架构\n", + "这种有问题的行为导致团队不同团队进行了两项工作。\n", + "\n", + "ASP.NET团队开始研究**Microsoft.Extensions.Http包,该包的主要功能是IHttpClientFactory**。这个针对HttpClient实例自用的工厂还包括基础HttpMessageHandler链的生命周期管理。如果您想了解有关此功能的更多信息,可以查看我的系列博客文章,我将在此介绍。\n", + "\n", + "IHttpClientFactory功能是作为ASP.NET Core 2.1的一部分发布的,对于许多人来说,这是一个很好的折衷方案,它解决了连接重用以及生命周期管理的问题。\n", + "\n", + "在同一时间范围内,.NET团队正在研究自己的解决方案。该团队也在.NET Core 2.1中发布,在HttpClient的处理程序链的核心引入了一个新的**SocketsHttpHandler**。该处理程序直接建立在Socket API之上,并在托管代码中实现HTTP。这项工作的一部分包括连接池系统以及为这些连接设置最大生存期的能力。\n", + "\n", + "![.NetFramework](./Assets/架构.003.png)\n", + "\n", + "说明:虽然默认情况下从.NET Core 2.1启用了SocketsHttpHandler,但实现仅限于HTTP / 1.1通信。那些需要HTTP / 2的用户必须禁用该功能并使用较旧的处理程序链,该处理程序链像以前一样依赖非托管代码,并且不包括连接池。\n", + "\n", + "幸运的是,.NET Core 3.0中已消除了此限制,并且现在提供了HTTP/2支持。这应该使用基于适合所有对象的SocketsHttpHandler链的HttpClient。\n", + "\n", + "结语:从.Net core 2.1开始,架构逐步稳定、完善。.Net 5、6、7、8中,逐渐发展出一整套完整机制,包括但不限于 连接池、工厂模式、Polly等。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 相关资源" + "## 后续\n", + "\n", + "后续将从基础使用到高级使用,一步步展开,一步步学习。" ] }, { diff --git a/Docs/1.2.使用准则.ipynb b/Docs/1.2.使用准则.ipynb new file mode 100644 index 0000000..6d28117 --- /dev/null +++ b/Docs/1.2.使用准则.ipynb @@ -0,0 +1,420 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用准则\n", + "System.Net.Http.HttpClient 类用于发送 HTTP 请求以及从 URI 所标识的资源接收 HTTP 响应。 HttpClient 实例是应用于该实例执行的所有请求的设置集合,每个实例使用自身的连接池,该池将其请求与其他请求隔离开来。 \n", + "\n", + "从 .NET Core 2.1 开始,SocketsHttpHandler 类提供实现,使行为在所有平台上保持一致。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、DNS 行为\n", + "HttpClient 仅在创建连接时解析 DNS。它不跟踪 DNS 服务器指定的任何生存时间 (TTL)。 \n", + "\n", + "如果 DNS 条目定期更改(这可能在某些方案中发生),客户端将不会遵循这些更新。 要解决此问题,可以通过设置 PooledConnectionLifetime 属性来限制连接的生存期,以便在替换连接时重复执行 DNS 查找。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.Net.Http;\n", + "\n", + "var handler = new SocketsHttpHandler\n", + "{\n", + " // Recreate every 15 minutes\n", + " PooledConnectionLifetime = TimeSpan.FromMinutes(15) \n", + "};\n", + "var sharedClient = new HttpClient(handler);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "上述 HttpClient 配置为重复使用连接 15 分钟。 PooledConnectionLifetime 指定的时间范围过后,系统会关闭连接,然后创建一个新连接。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2、共用连接(底层自动管理连接池)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "HttpClient 的连接池链接到其基础 SocketsHttpHandler。 \n", + "释放 HttpClient 实例时,它会释放池中的所有现有连接。 如果稍后向同一服务器发送请求,则必须重新创建一个新连接。 \n", + "因此,创建不必要的连接会导致性能损失。 \n", + "此外,TCP 端口不会在连接关闭后立即释放。 (有关这一点的详细信息,请参阅 RFC 9293 中的 TCP TIME-WAIT。)如果请求速率较高,则可用端口的操作系统限制可能会耗尽。 \n", + "\n", + "为了避免端口耗尽问题,建议将 HttpClient 实例重用于尽可能多的 HTTP 请求。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 什么是连接池\n", + "SocketsHttpHandler为每个唯一端点建立连接池,您的应用程序通过HttpClient向该唯一端点发出出站HTTP请求。在对端点的第一个请求上,当不存在现有连接时,将建立一个新的HTTP连接并将其用于该请求。该请求完成后,连接将保持打开状态并返回到池中。\n", + "\n", + "对同一端点的后续请求将尝试从池中找到可用的连接。如果没有可用的连接,并且尚未达到该端点的连接限制,则将建立新的连接。达到连接限制后,请求将保留在队列中,直到连接可以自由发送它们为止。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 如何控制连接池" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "有三个主要设置可用于控制连接池的行为。\n", + "\n", + "+ PooledConnectionLifetime,定义连接在池中保持活动状态的时间。此生存期到期后,将不再为将来的请求而合并或发出连接。\n", + "\n", + "+ PooledConnectionIdleTimeout,定义闲置连接在未使用时在池中保留的时间。一旦此生存期到期,空闲连接将被清除并从池中删除。\n", + "\n", + "+ MaxConnectionsPerServer,定义每个端点将建立的最大出站连接数。每个端点的连接分别池化。例如,如果最大连接数为2,则您的应用程序将请求发送到两个www.github.com和www.google.com,总共可能最多有4个打开的连接。\n", + "\n", + "默认情况下,从.NET Core 2.1开始,更高级别的HttpClientHandler将SocketsHttpHandler用作内部处理程序。没有任何自定义配置,将应用连接池的默认设置。\n", + "\n", + "该**PooledConnectionLifetime默认是无限的,因此,虽然经常使用的请求,连接可能会无限期地保持打开状态。该PooledConnectionIdleTimeout默认为2分钟,如果在连接池中长时间未使用将被清理。MaxConnectionsPerServer**默认为int.MaxValue,因此连接基本上不受限制。\n", + "\n", + "如果希望控制这些值中的任何一个,则可以手动创建SocketsHttpHandler实例,并根据需要进行配置。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.Net.Http;\n", + "var socketsHandler = new SocketsHttpHandler\n", + "{\n", + "\tPooledConnectionLifetime = TimeSpan.FromMinutes(10),\n", + "\tPooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),\n", + "\tMaxConnectionsPerServer = 10\n", + "};\n", + "\t\n", + "var client = new HttpClient(socketsHandler);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "在前面的示例中,对SocketsHttpHandler进行了配置,以使连接将最多在10分钟后停止重新发出并关闭。如果闲置5分钟,则连接将在池的清理过程中被更早地删除。我们还将最大连接数(每个端点)限制为十个。如果我们需要并行发出更多出站请求,则某些请求可能会排队等待,直到10个池中的连接可用为止。\n", + "要应用处理程序,它将被传递到HttpClient的构造函数中。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 测试连接寿命" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "using System.Net;\n", + "using System.Net.Http;\n", + "\n", + "var ips = await Dns.GetHostAddressesAsync(\"www.hao123.com\");\n", + "string firstIp = ips.FirstOrDefault().ToString();\n", + "\t\n", + "foreach (var ipAddress in ips)\n", + "{\n", + " Console.WriteLine(ipAddress.MapToIPv4().ToString());\n", + "}\n", + "\n", + "//自定义行为\n", + "var socketsHandler = new SocketsHttpHandler\n", + "{\n", + " //连接池生命周期为10分钟:连接在池中保持活动时间为10分钟\n", + " PooledConnectionLifetime = TimeSpan.FromMinutes(10),\n", + "\n", + " //池化链接的空闲超时时间为5分钟: 5分钟内连接不被重用,则被释放后销毁\n", + " PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),\n", + " \n", + " //每端点的最大连接数设置为10个\n", + " MaxConnectionsPerServer = 10\n", + "};\n", + "\n", + "var client = new HttpClient(socketsHandler);\n", + "\n", + "for (var i = 0; i < 5; i++)\n", + "{\n", + " _ = await client.GetAsync(\"https://www.hao123.com\");\n", + " await Task.Delay(TimeSpan.FromSeconds(2));\n", + "}\n", + "\n", + "Console.WriteLine(\"请在程序退出后,执行下面命令行查看网络情况\");\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "使用我们刚刚讨论的设置,此代码依次向同一端点发出5个请求。在每个请求之间,它会暂停两秒钟。该代码还输出从DNS检索到的Google服务器的IPv4地址。我们可以使用此IP地址来查看通过PowerShell中发出的netstat命令对其打开的连接:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:firstIp --name queryIp\n", + "Write-Host \"请先执行上面的单元,再执行本单元\"\n", + "Write-Host \"异常话,很可能是:未查找IP为 $queryIp 的网络状\"\n", + "\n", + "netstat -ano | findstr $queryIp" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3、推荐使用方式" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:ips --name ips\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:ips --name ips\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:ips --name ips\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:firstIp --name firstIp\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:firstIp --name firstIp\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "javascript" + }, + "polyglot_notebook": { + "kernelName": "javascript" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @csharp:firstIp --name firstIp\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "#!set --value @pwsh:$sss --name $sss\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb new file mode 100644 index 0000000..da89958 --- /dev/null +++ b/Docs/1.3.0.基础使用.管理客户端.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 基础使用" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、说明" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.3.1.基础使用.发送请求.ipynb b/Docs/1.3.1.基础使用.发送请求.ipynb new file mode 100644 index 0000000..8a16ebd --- /dev/null +++ b/Docs/1.3.1.基础使用.发送请求.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# 基础使用-发送请求" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、创建客户端" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.2.使用原则.ipynb b/Docs/1.3.2.基础使用.使用请求体.ipynb similarity index 100% rename from Docs/1.2.使用原则.ipynb rename to Docs/1.3.2.基础使用.使用请求体.ipynb diff --git a/Docs/1.3.3.基础使用.处理响应.ipynb b/Docs/1.3.3.基础使用.处理响应.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.3.3.基础使用.处理响应.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.3.4.基础使用.处理错误.ipynb b/Docs/1.3.4.基础使用.处理错误.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.3.4.基础使用.处理错误.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.3.5.基础使用.使用代理.ipynb b/Docs/1.3.5.基础使用.使用代理.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.3.5.基础使用.使用代理.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.3.6.基础使用.使用Json.ipynb b/Docs/1.3.6.基础使用.使用Json.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.3.6.基础使用.使用Json.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.3.7.基础使用.使用Cookie.ipynb b/Docs/1.3.7.基础使用.使用Cookie.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.3.7.基础使用.使用Cookie.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.0.高级使用.概述.ipynb b/Docs/1.4.0.高级使用.概述.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.0.高级使用.概述.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.1.高级使用.初始化.ipynb b/Docs/1.4.1.高级使用.初始化.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.1.高级使用.初始化.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.2.高级使用.连接池.ipynb b/Docs/1.4.2.高级使用.连接池.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.2.高级使用.连接池.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.3.高级使用.重复使用.ipynb b/Docs/1.4.3.高级使用.重复使用.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.3.高级使用.重复使用.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.4.高级使用.使用管道.ipynb b/Docs/1.4.4.高级使用.使用管道.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.4.高级使用.使用管道.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.5.高级使用.类型化客户端.ipynb b/Docs/1.4.5.高级使用.类型化客户端.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.5.高级使用.类型化客户端.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.6..高级使用.工厂模式.ipynb b/Docs/1.4.6..高级使用.工厂模式.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.6..高级使用.工厂模式.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.4.7.高级使用.Polly.ipynb b/Docs/1.4.7.高级使用.Polly.ipynb new file mode 100644 index 0000000..7656438 --- /dev/null +++ b/Docs/1.4.7.高级使用.Polly.ipynb @@ -0,0 +1,58 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 使用原则" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、复用" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/1.5.总结.ipynb b/Docs/1.5.总结.ipynb new file mode 100644 index 0000000..92d6ad9 --- /dev/null +++ b/Docs/1.5.总结.ipynb @@ -0,0 +1,69 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + } + }, + "source": [ + "# HttpClient 总结" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "source": [ + "## 1、系统结束,也是新的开始" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "python" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Docs/Assets/架构.001.png b/Docs/Assets/架构.001.png new file mode 100644 index 0000000..310fcdc Binary files /dev/null and b/Docs/Assets/架构.001.png differ diff --git a/Docs/Assets/架构.002.png b/Docs/Assets/架构.002.png new file mode 100644 index 0000000..356d8be Binary files /dev/null and b/Docs/Assets/架构.002.png differ diff --git a/Docs/Assets/架构.003.png b/Docs/Assets/架构.003.png new file mode 100644 index 0000000..b3127dc Binary files /dev/null and b/Docs/Assets/架构.003.png differ diff --git a/Docs/Assets/架构.004.png b/Docs/Assets/架构.004.png new file mode 100644 index 0000000..3cab98f Binary files /dev/null and b/Docs/Assets/架构.004.png differ diff --git a/HttpClientStudy.Core/UseFactory/UseFactoryDemo.cs b/HttpClientStudy.Core/UseFactory/UseFactoryDemo.cs new file mode 100644 index 0000000..d1b6130 --- /dev/null +++ b/HttpClientStudy.Core/UseFactory/UseFactoryDemo.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HttpClientStudy.Core.UseFactory +{ + public class UseFactoryDemo + { + + } +} diff --git a/HttpClientStudy.sln b/HttpClientStudy.sln index 8097e05..ecf14c2 100644 --- a/HttpClientStudy.sln +++ b/HttpClientStudy.sln @@ -16,7 +16,24 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{56D9132E-6D9B-4D4B-B82C-D8F74AA4373A}" ProjectSection(SolutionItems) = preProject Docs\1.1.概述.ipynb = Docs\1.1.概述.ipynb - Docs\1.2.使用原则.ipynb = Docs\1.2.使用原则.ipynb + Docs\1.2.使用准则.ipynb = Docs\1.2.使用准则.ipynb + Docs\1.3.0.基础使用.管理客户端.ipynb = Docs\1.3.0.基础使用.管理客户端.ipynb + Docs\1.3.1.基础使用.发送请求.ipynb = Docs\1.3.1.基础使用.发送请求.ipynb + Docs\1.3.2.基础使用.使用请求体.ipynb = Docs\1.3.2.基础使用.使用请求体.ipynb + Docs\1.3.3.基础使用.处理响应.ipynb = Docs\1.3.3.基础使用.处理响应.ipynb + Docs\1.3.4.基础使用.处理错误.ipynb = Docs\1.3.4.基础使用.处理错误.ipynb + Docs\1.3.5.基础使用.使用代理.ipynb = Docs\1.3.5.基础使用.使用代理.ipynb + Docs\1.3.6.基础使用.使用Json.ipynb = Docs\1.3.6.基础使用.使用Json.ipynb + Docs\1.3.7.基础使用.使用Cookie.ipynb = Docs\1.3.7.基础使用.使用Cookie.ipynb + Docs\1.4.0.高级使用.概述.ipynb = Docs\1.4.0.高级使用.概述.ipynb + Docs\1.4.1.高级使用.初始化.ipynb = Docs\1.4.1.高级使用.初始化.ipynb + Docs\1.4.2.高级使用.连接池.ipynb = Docs\1.4.2.高级使用.连接池.ipynb + Docs\1.4.3.高级使用.重复使用.ipynb = Docs\1.4.3.高级使用.重复使用.ipynb + Docs\1.4.4.高级使用.使用管道.ipynb = Docs\1.4.4.高级使用.使用管道.ipynb + Docs\1.4.5.高级使用.类型化客户端.ipynb = Docs\1.4.5.高级使用.类型化客户端.ipynb + Docs\1.4.6..高级使用.工厂模式.ipynb = Docs\1.4.6..高级使用.工厂模式.ipynb + Docs\1.4.7.高级使用.Polly.ipynb = Docs\1.4.7.高级使用.Polly.ipynb + Docs\1.5.总结.ipynb = Docs\1.5.总结.ipynb Docs\说明.md = Docs\说明.md EndProjectSection EndProject