学海无涯

导航

统计

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

 

posted on   宁静致远.  阅读(18)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示