- 持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
前言
后端服务在很多情况下需要发起一些HTTP请求,如调用第三方接口(微信、支付宝、高德)等等这些外部服务,无法直接在项目内实现的时候,这个时候我们就需要使用HTTP请求,在项目内部请求外部服务。
我们可以借助 .NET 提供的 IHttpClientFactory 发出 HTTP 请求
一、为什么要使用IHttpClientFactory
了解.NET的同学应该知道,使用 HttpClient 可以发起,为什么要使用 IHttpClientFactory 呢? IHttpClientFactory 的优势如下:
- 提供一个中心位置,用于命名和配置逻辑 HttpClient 实例。
- 通过 HttpClient 中的委托处理程序来编码出站中间件的概念。 提供基于 Polly 的中间件的扩展,以利用 HttpClient 中的委托处理程序。
- 管理基础 HttpClientMessageHandler 实例的池和生存期。 自动管理可避免手动管理 HttpClient 生存期时出现的常见 DNS(域名系统)问题。
- 日志记录
二、如何使用
- 将服务添加至容器中
// 在Program启动类注册IHttpClientFactory
builder.Services.AddHttpClient();
- 在你使用服务的地方依赖注入IHttpClientFactory
// 使用依赖注入(DI)
private readonly IHttpClientFactory _httpClientFactory;
public WeatherForecastController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
// 测试控制器请求Get方法
public async Task Get()
{
// 创建HttpClient实例
var client = _httpClientFactory.CreateClient();
// 使用SendAsync发送带有指定请求的 HTTP 请求。
var responseMessage = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get,"Url"));
// 判断请求返回是否正常(return ((int)_statusCode >= 200) && ((int)_statusCode <= 299))
if (responseMessage.IsSuccessStatusCode)
{
// 将 HTTP 内容序列化并返回将内容表示为异步操作的流
using var contentStream = await responseMessage.Content.ReadAsStreamAsync();
// 反序列化为你想要的内容
var result = await JsonSerializer.DeserializeAsync<T>(contentStream);
}
}
- 在很多场景下,如授权需要添加请求头,那么如何实现添加请求头呢?
var responseMessage = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "Url")
{
Headers =
{
{ "Authorization","Bearer ............" }
}
});
- 命名客户端,应用需要
HttpClient
的许多不同用法。
builder.Services.AddHttpClient("WeChat", httpClient =>
{
// 基础请求地址为 `https://xxx.com/`。
httpClient.BaseAddress = new Uri("https://xxx.com/");
// 添加默认请求头
httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "");
httpClient.DefaultRequestHeaders.Add("Authorization","Bearer ............");
});
- 除了GET谓词之外,还有POST/PUT/DELETE等等,如何实现?
// 1.为序列化参数对象 2.字符编码 3.格式
var strJson = new StringContent(
JsonSerializer.Serialize(new { Id = Guid.NewGuid(),Name = "张三" }),
Encoding.UTF8,
Application.Json);
using var responseMessage = await client.PostAsync("Url", strJson);
// Delete请求
using var responseMessage = await _httpClient.DeleteAsync($"Url/{Id}");
三、使用基于 Polly 的处理程序
Polly 是适用于 .NET 的全面恢复和临时故障处理库。 开发人员通过它可以表达策略,例如以流畅且线程安全的方式处理重试、断路器、超时、Bulkhead 隔离和回退。
- 安装 Polly Nuget包
AddTransientHttpErrorPolicy
允许定义一个策略来处理暂时性错误
builder.Services.AddHttpClient("WeChat", httpClient =>
{
// 基础请求地址为 `https://xxx.com/`。
httpClient.BaseAddress = new Uri("https://xxx.com/");
})
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
如上创建了一个策略来处理瞬态故障,如有必要,重试底层 http 请求最多 3 次。该策略将在第一次重试之前应用 1 秒的延迟;第二次重试前 5 秒;第三次前10秒
- 动态选择策略,如你只想给Get请求添加重试机制,但并不想给一些POST/PUT/DELETE等请求添加怎么处理?
builder.Services.AddHttpClient("Test").AddPolicyHandler(httpRequestMessage =>
httpRequestMessage.Method == HttpMethod.Get ? "策略A" : "策略NoOp");
// NoOp 策略只是按原样执行底层调用,没有任何额外的策略行为
var noOpPolicy = Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();
- 应用多个策略,即嵌套多个 Polly 策略
// 嵌套多个直接使用两个 AddTransientHttpErrorPolicy 即可
builder.Services.AddHttpClient("Test").AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}))
.AddTransientHttpErrorPolicy(builder => builder.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30)
));
- 关于嵌套多个策略的应用顺序
当您配置多个策略时,这些策略将应用于从外部(首先配置)到内部(最后配置)顺序的每个调用。
图1-1为 Polly 官方示例
总结
如上即是.NET程序HTTP请求的一些基础用法,以及一些进阶的使用,是不是非常方便!借助 IHttpClientFactory 工厂 比直接使用HttpClient的优势要更加明显。