使用 IHttpClientFactory 处理请求和响应
HttpClient 类型是在 2012 年发布的 .NET Framework 4.5 中引入的。 换句话说,它已经存在一段时间了。 HttpClient 用于从由 Uri 标识的网络资源发出 HTTP 请求和处理 HTTP 响应。 HTTP 协议占所有 Internet 流量的绝大部分。
根据推动最佳做法的新式应用程序开发原则,IHttpClientFactory 充当工厂抽象,可以使用自定义配置创建 HttpClient 实例。
.NET Core 2.1 中引入了 IHttpClientFactory。 常见的基于 HTTP 的 .NET 工作负载可以轻松利用可复原和瞬态故障处理第三方中间件。
IHttpClientFactory 创建的 HttpClient 实例的生存期管理与手动创建的实例完全不同。 策略是使用由 IHttpClientFactory 创建的短期客户端,或设置了 PooledConnectionLifetime 的长期客户端。
IHttpClientFactory 类型
安装 Microsoft.Extensions.Http NuGet 包
调用任何 AddHttpClient 扩展方法时,将 IHttpClientFactory 和相关服务添加到 IServiceCollection。 IHttpClientFactory 类型具有以下优点:
- 将
HttpClient类公开为 DI 就绪类型。 - 提供一个中心位置,用于命名和配置逻辑
HttpClient实例。 - 通过
HttpClient中的委托处理程序来编码出站中间件的概念。 - 提供基于 Polly 的中间件的扩展方法,以利用
HttpClient中的委托处理程序。 - 管理基础 HttpClientHandler 实例的缓存和生存期。 自动管理可避免手动管理
HttpClient生存期时出现的常见域名系统 (DNS) 问题。 - (通过 ILogger)添加可配置的记录体验,以处理工厂创建的客户端发送的所有请求。
使用模式
在应用中可以通过以下多种方式使用 IHttpClientFactory:
基本用法
若要注册 IHttpClientFactory,请在 Program.cs 文件中调用 AddHttpClient:
builder.Services.AddHttpClient();
使用服务可能需要 IHttpClientFactory 作为带有 DI 的构造函数参数。 以下代码使用 IHttpClientFactory 来创建 HttpClient 实例:
命名客户端
在以下情况下,命名客户端是一个不错的选择:
- 应用需要
HttpClient的许多不同用法。 - 许多
HttpClient实例具有不同的配置。
可以在 IServiceCollection 上注册时指定命名 HttpClient 的配置:
string? httpClientName = builder.Configuration["TodoHttpClientName"];
if (string.IsNullOrEmpty(httpClientName))
{
throw new ArgumentException("HttpClientName cannot be null or empty", nameof(httpClientName));
}
builder.Services.AddHttpClient(httpClientName, client =>
{
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
//client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
配置文件 appsettings.json :
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
创建客户端
每次调用 CreateClient 时:
- 创建
HttpClient的新实例。 - 调用配置操作。
要创建命名客户端,请将其名称传递到 CreateClient 中:
using System.Net.Http; using System.Text.Json; using HelperToolProject.Core.ProjectAggregate; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace HelperToolProject.Web.Api; [Route("api/[controller]")] [ApiController] public class TodoController : ControllerBase { ILogger<TodoController> _logger; IConfiguration _configuration; IHttpClientFactory _httpClientFactory; public TodoController(IHttpClientFactory httpClientFactory, ILogger<TodoController> logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; _httpClientFactory = httpClientFactory; } [HttpGet("UserTodoes")] public async Task<ActionResult<IEnumerable<ToDo>?>> GetUserTodoesAsync() {//1.基本用法 string uri = $"https://jsonplaceholder.typicode.com/todos/"; using HttpClient httpClient = _httpClientFactory.CreateClient(); try { var todos = await httpClient.GetFromJsonAsync<IEnumerable<ToDo>>(uri, new JsonSerializerOptions(JsonSerializerDefaults.Web)); return Ok(todos); } catch (Exception ex) { _logger.LogError(ex, "Error getting todos"); return StatusCode(StatusCodes.Status500InternalServerError, "Error getting todos"); } } [HttpGet("{userId:int}")] public async Task<ActionResult<IEnumerable<ToDo>?>> GetUserTodoAsync(int userId) {//2.指定HttpClient名称用法,场景:请求多个不同的网址,或者请求同一个网址但是需要不同的配置 string uri = $"todos?userId={userId}"; string? httpClientName = _configuration["TodoHttpClientName"]; using HttpClient httpClient = _httpClientFactory.CreateClient(httpClientName ?? ""); try { var todo = await httpClient.GetFromJsonAsync<IEnumerable<ToDo>?>(uri, new JsonSerializerOptions(JsonSerializerDefaults.Web)); return Ok(todo); } catch (Exception ex) { _logger.LogError(ex, "Error getting todo"); return StatusCode(StatusCodes.Status500InternalServerError, "Error getting todo"); } } } public class ToDo { public int UserId { get; set; } public int Id { get; set; } public string Title { get; set; } public bool Completed { get; set; } }
发出 POST、PUT 和 DELETE 请求
using System.Net.Http; using System.Net.Mime; using System.Text; using System.Text.Json; using HelperToolProject.Core.ProjectAggregate; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace HelperToolProject.Web.Api; [Route("api/[controller]")] [ApiController] public class TodoController : ControllerBase { ILogger<TodoController> _logger; IConfiguration _configuration; IHttpClientFactory _httpClientFactory; string? httpClientName; public TodoController(IHttpClientFactory httpClientFactory, ILogger<TodoController> logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; _httpClientFactory = httpClientFactory; httpClientName = _configuration["TodoHttpClientName"]; } [HttpGet("UserTodoes")] public async Task<ActionResult<IEnumerable<ToDo>?>> GetUserTodoesAsync() {//1.基本用法 string uri = $"https://jsonplaceholder.typicode.com/todos/"; using HttpClient httpClient = _httpClientFactory.CreateClient(); try { var todos = await httpClient.GetFromJsonAsync<IEnumerable<ToDo>>(uri, new JsonSerializerOptions(JsonSerializerDefaults.Web)); return Ok(todos); } catch (Exception ex) { _logger.LogError(ex, "Error getting todos"); return StatusCode(StatusCodes.Status500InternalServerError, "Error getting todos"); } } [HttpGet("{userId:int}")] public async Task<ActionResult<IEnumerable<ToDo>?>> GetUserTodoAsync(int userId) {//2.指定HttpClient名称用法,场景:请求多个不同的网址,或者请求同一个网址但是需要不同的配置 string uri = $"todos?userId={userId}"; using HttpClient httpClient = _httpClientFactory.CreateClient(httpClientName ?? ""); try { var todo = await httpClient.GetFromJsonAsync<IEnumerable<ToDo>?>(uri, new JsonSerializerOptions(JsonSerializerDefaults.Web)); return Ok(todo); } catch (Exception ex) { _logger.LogError(ex, "Error getting todo"); return StatusCode(StatusCodes.Status500InternalServerError, "Error getting todo"); } } [HttpPost] public async Task CreateItemAsync(ToDo item) { using HttpClient httpClient = _httpClientFactory.CreateClient(httpClientName ?? ""); using StringContent json = new( JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)), Encoding.UTF8, MediaTypeNames.Application.Json); using HttpResponseMessage httpResponse = await httpClient.PostAsync("/api/items", json); httpResponse.EnsureSuccessStatusCode(); } [HttpPut] public async Task UpdateItemAsync(ToDo item) { using HttpClient httpClient = _httpClientFactory.CreateClient(httpClientName ?? ""); using StringContent json = new( JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)), Encoding.UTF8, MediaTypeNames.Application.Json); using HttpResponseMessage httpResponse = await httpClient.PutAsync($"/api/items/{item.Id}", json); httpResponse.EnsureSuccessStatusCode(); } [HttpDelete("id:int")] public async Task DeleteItemAsync(int id) { using HttpClient httpClient = _httpClientFactory.CreateClient(httpClientName ?? ""); using HttpResponseMessage httpResponse = await httpClient.DeleteAsync($"/api/items/{id}"); httpResponse.EnsureSuccessStatusCode(); } } public class ToDo { public int UserId { get; set; } public int Id { get; set; } public string Title { get; set; } public bool Completed { get; set; } }
参考网址:https://learn.microsoft.com/zh-cn/dotnet/core/extensions/httpclient-factory
浙公网安备 33010602011771号