使用 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
:
1 | builder.Services.AddHttpClient(); |
使用服务可能需要 IHttpClientFactory
作为带有 DI 的构造函数参数。 以下代码使用 IHttpClientFactory
来创建 HttpClient
实例:
命名客户端
在以下情况下,命名客户端是一个不错的选择:
- 应用需要
HttpClient
的许多不同用法。 - 许多
HttpClient
实例具有不同的配置。
可以在 IServiceCollection
上注册时指定命名 HttpClient
的配置:
1 2 3 4 5 6 7 8 9 10 | 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 :
1 2 3 | { "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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?