使用 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號