在 ASP.NET Core 中使用 IHttpClientFactory 发出 HTTP 请求

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在 ASP.NET Core 中使用 IHttpClientFactory 发出 HTTP 请求相关的知识,希望对你有一定的参考价值。

参考技术A 可以注册 IHttpClientFactory 并将其用于配置和创建应用中的 HttpClient 实例。 IHttpClientFactory 的优势如下:

查看或下载示例代码(如何下载)。

此主题版本中的示例代码使用 System.Text.Json 来对 HTTP 响应中返回的 JSON 内容进行反序列化。 对于使用 Json.NET 和 ReadAsAsync 的示例,请使用版本选择器选择此主题的 2.x 版本。

在应用中可以通过以下多种方式使用 IHttpClientFactory:

最佳方法取决于应用要求。

可以通过调用 AddHttpClient 来注册 IHttpClientFactory:

可以使用依赖项注入 (DI) 来请求 IHttpClientFactory。 以下代码使用 IHttpClientFactory 来创建 HttpClient 实例:

像前面的示例一样,使用 IHttpClientFactory 是重构现有应用的好方法。 这不会影响 HttpClient 的使用方式。 在现有应用中创建 HttpClient 实例的位置,使用对 CreateClient 的调用替换这些匹配项。

在以下情况下,命名客户端是一个不错的选择:

可以在 Startup.ConfigureServices 中注册时指定命名 HttpClient 的配置:

在上述代码中,客户端配置如下:

每次调用 CreateClient 时:

要创建命名客户端,请将其名称传递到 CreateClient 中:

在上述代码中,请求不需要指定主机名。 代码可以仅传递路径,因为采用了为客户端配置的基址。

类型化客户端:

类型化客户端在构造函数中接受 HttpClient 参数:

在上述代码中:

可以创建特定于 API 的方法来公开 HttpClient 功能。 例如,创建 GetAspNetDocsIssues 方法来封装代码以检索未解决的问题。

以下代码调用 Startup.ConfigureServices 中的 AddHttpClient 来注册类型化客户端类:

使用 DI 将类型客户端注册为暂时客户端。 在上述代码中,AddHttpClient 将 GitHubService 注册为暂时性服务。 此注册使用工厂方法执行以下操作:

可以直接插入或使用类型化客户端:

可以在 Startup.ConfigureServices 中注册时指定类型化客户端的配置,而不是在类型化客户端的构造函数中指定:

可以将 HttpClient 封装在类型化客户端中, 定义一个在内部调用 HttpClient 实例的方法,而不是将其公开为属性:

在上述代码中,HttpClient 存储在私有字段中。 通过公共 GetRepos 方法访问 HttpClient。

IHttpClientFactory 可结合第三方库(例如 Refit)使用。 Refit 是.NET 的 REST 库。 它将 REST API 转换为实时接口。 RestService 动态生成该接口的实现,使用 HttpClient 进行外部 HTTP 调用。

定义了接口和答复来代表外部 API 及其响应:

可以添加类型化客户端,使用 Refit 生成实现:

可以在必要时使用定义的接口,以及由 DI 和 Refit 提供的实现:

在前面的示例中,所有 HTTP 请求均使用 GET HTTP 谓词。 HttpClient 还支持其他 HTTP 谓词,其中包括:

有关受支持的 HTTP 谓词的完整列表,请参阅 HttpMethod。

下面的示例演示如何发出 HTTP POST 请求:

在前面的代码中,CreateItemAsync 方法:

HttpClient 还支持其他类型的内容。 例如,MultipartContent 和 StreamContent。 有关受支持的内容的完整列表,请参阅 HttpContent。

下面的示例演示了一个 HTTP PUT 请求:

前面的代码与 POST 示例非常相似。 SaveItemAsync 方法调用 PutAsync 而不是 PostAsync。

下面的示例演示了一个 HTTP DELETE 请求:

在前面的代码中,DeleteItemAsync 方法调用 DeleteAsync。 由于 HTTP DELETE 请求通常不包含正文,因此 DeleteAsync 方法不提供接受 HttpContent 实例的重载。

要详细了解如何将不同的 HTTP 谓词用于 HttpClient,请参阅 HttpClient。

HttpClient 具有委托处理程序的概念,这些委托处理程序可以链接在一起,处理出站 HTTP 请求。 IHttpClientFactory:

创建委托处理程序:

上述代码检查请求中是否存在 X-API-KEY 标头。 如果缺失 X-API-KEY,则返回 BadRequest。

可使用 Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddHttpMessageHandler 将多个处理程序添加到 HttpClient 的配置中:

在上述代码中通过 DI 注册了 ValidateHeaderHandler。 注册后可以调用 AddHttpMessageHandler,传入标头的类型。

可以按处理程序应该执行的顺序注册多个处理程序。 每个处理程序都会覆盖下一个处理程序,直到最终 HttpClientHandler 执行请求:

当 IHttpClientFactory 创建新的委托处理程序时,它使用 DI 来完成处理程序的构造函数参数。 IHttpClientFactory 为每个处理程序创建单独的 DI 范围,当处理程序使用限定范围的服务时,这可能导致意外的行为。

例如,请考虑下面的接口及其实现,它将任务表示为带有标识符 OperationId 的操作:

顾名思义,使用限定范围的生存期向 DI 注册 IOperationScoped:

以下委托处理程序消耗并使用 IOperationScoped 来设置传出请求的 X-OPERATION-ID 标头:

在HttpRequestsSample下载] 中,导航到 /Operation 并刷新页面。 每个请求的请求范围值发生更改,但处理程序范围值仅每 5 秒钟更改一次。

处理程序可依赖于任何作用域的服务。 处理程序依赖的服务会在处置处理程序时得到处置。

使用以下方法之一将每个请求状态与消息处理程序共享:

IHttpClientFactory 与第三方库 Polly 集成。 Polly 是适用于 .NET 的全面恢复和临时故障处理库。 开发人员通过它可以表达策略,例如以流畅且线程安全的方式处理重试、断路器、超时、Bulkhead 隔离和回退。

提供了扩展方法,以实现将 Polly 策略用于配置的 HttpClient 实例。 Polly 扩展支持将基于 Polly 的处理程序添加到客户端。 Polly 需要 Microsoft.Extensions.Http.Polly NuGet 包。

错误通常在暂时执行外部 HTTP 调用时发生。 AddTransientHttpErrorPolicy 允许定义一个策略来处理暂时性错误。 使用 AddTransientHttpErrorPolicy 配置的策略处理以下响应:

AddTransientHttpErrorPolicy 提供对 PolicyBuilder 对象的访问权限,该对象配置为处理表示可能的临时故障的错误:

上述代码中定义了 WaitAndRetryAsync 策略。 请求失败后最多可以重试三次,每次尝试间隔 600 ms。

提供了扩展方法来添加基于 Polly 的处理程序,例如 AddPolicyHandler。 以下 AddPolicyHandler 重载检查请求以确定要应用的策略:

在上述代码中,如果出站请求为 HTTP GET,则应用 10 秒超时。 其他所有 HTTP 方法应用 30 秒超时。

这对嵌套 Polly 策略很常见:

在上面的示例中:

管理常用策略的一种方法是一次性定义它们并使用 PolicyRegistry 注册它们。

在以下代码中:

C#

有关 IHttpClientFactory 和 Polly 集成的详细信息,请参阅 Polly Wiki。

每次对 IHttpClientFactory 调用 CreateClient 都会返回一个新 HttpClient 实例。 每个命名客户端都创建一个 HttpMessageHandler。 工厂管理 HttpMessageHandler 实例的生存期。

IHttpClientFactory 将工厂创建的 HttpMessageHandler 实例汇集到池中,以减少资源消耗。 新建 HttpClient 实例时,可能会重用池中的 HttpMessageHandler 实例(如果生存期尚未到期的话)。

由于每个处理程序通常管理自己的基础 HTTP 连接,因此需要池化处理程序。 创建超出必要数量的处理程序可能会导致连接延迟。 部分处理程序还保持连接无期限地打开,这样可以防止处理程序对 DNS(域名系统)更改作出反应。

处理程序的默认生存期为两分钟。 可在每个命名客户端上重写默认值:

HttpClient 实例通常可视为无需处置的 .NET 对象。 处置既取消传出请求,又保证在调用 Dispose 后无法使用给定的 HttpClient 实例。 IHttpClientFactory 跟踪和处置 HttpClient 实例使用的资源。

保持各个 HttpClient 实例长时间处于活动状态是在 IHttpClientFactory 推出前使用的常见模式。 迁移到 IHttpClientFactory 后,就无需再使用此模式。

通过在启用了 DI 的应用中使用 IHttpClientFactory,可避免:

此外,还有其他方法使用生命周期长的 SocketsHttpHandler 实例来解决上述问题。

上述方法使用 IHttpClientFactory 解决问题的类似方式解决资源管理问题。

共用 HttpMessageHandler 实例将导致共享 CookieContainer 对象。 意外的 CookieContainer 对象共享通常会导致错误的代码。 对于需要 cookie 的应用,请考虑执行以下任一操作:

调用 ConfigurePrimaryHttpMessageHandler 以禁用自动 cookie 处理:

通过 IHttpClientFactory 创建的客户端记录所有请求的日志消息。 在日志记录配置中启用合适的信息级别可以查看默认日志消息。 仅在跟踪级别包含附加日志记录(例如请求标头的日志记录)。

用于每个客户端的日志类别包含客户端名称。 例如,名为 MyNamedClient 的客户端记录类别为“System.Net.Http.HttpClient.MyNamedClient.LogicalHandler”的消息。 后缀为 LogicalHandler 的消息在请求处理程序管道外部发生。 在请求时,在管道中的任何其他处理程序处理请求之前记录消息。 在响应时,在任何其他管道处理程序接收响应之后记录消息。

日志记录还在请求处理程序管道内部发生。 在 MyNamedClient 示例中,这些消息的日志类别为“System.Net.Http.HttpClient.MyNamedClient.ClientHandler”。 在请求时,在所有其他处理程序运行后,以及刚好要发出请求之前记录消息。 在响应时,此日志记录包含响应在通过处理程序管道被传递回去之前的状态。

在管道内外启用日志记录,可以检查其他管道处理程序做出的更改。 这可能包含对请求标头的更改,或者对响应状态代码的更改。

通过在日志类别中包含客户端名称,可以对特定的命名客户端筛选日志。

控制客户端使用的内部 HttpMessageHandler 的配置是有必要的。

在添加命名客户端或类型化客户端时,会返回 IHttpClientBuilder。 ConfigurePrimaryHttpMessageHandler 扩展方法可以用于定义委托。 委托用于创建和配置客户端使用的主要 HttpMessageHandler:

在控制台中,将以下包引用添加到项目中:

如下示例中:

标头传播是一个 ASP.NET Core 中间件,可将 HTTP 标头从传入请求传播到传出 HTTP 客户端请求。 使用标头传播:

以上是关于在 ASP.NET Core 中使用 IHttpClientFactory 发出 HTTP 请求的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

在 ASP.NET Core 6.0 中使用 Serilog

如何在 ASP.Net Core 中使用 IHostedService

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

asp.net core中托管SPA应用

如何使用 EF Core 在 ASP.NET Core 中取消应用迁移