在 ASP.NET Core 的外部库中使用 HttpClient

Using HttpClient in external library for ASP.NET Core

简介

我正在开发 SDK(这只是 REST API 的包装器)。此 SDK 在内部使用 HttpClient 为 API 发出请求。 SDK 以 .NET Standard 2.1 为目标。这是简化的代码。

public class SomeSdk 
{
    private readonly HttpClient _httpClient;

    public SomeSdk(<some-settings>) 
    {
        _httpClient = new HttpClient(<some-settings>);
    }

    public async Task<string> GetSomethingFromApiAsync() 
    {
        return await _httpClient.GetStringAsync(...);
    }
}

问题

其他开发人员正在 ASP.NET 核心项目中使用该 SDK。我知道 HttpClient 的问题,我知道 MS DOCS 建议注入 HttpClientFactory 以避免 SocketException 并反映 DNS 更改。

第一个想到的解决方案是让用户在SDK构造函数或其他方法中传递HttpClient(即来自HttpClientFactory)。像这样。

public async Task<string> GetSomethingFromApiAsync(HttpClient clientFromFactory) 
{
    return await clientFromFactory.GetStringAsync(...);
}

然而,SDK 在使用 HttpClient 之前会对其进行配置(代理、超时等),因此存在问题,因此应将其初始化保留在 SDK 构造函数中。

问题

我目前的解决方案是在 ASP.NET 核心项目中将 SDK 作为单例注入并接受 DNS 反射问题。我还是想问问有没有人会推荐我一个更好的方法来解决这个问题。

非常感谢,亚当。

实例化 HttpClient 的更好方法是使用 DI。
因此,假设您的 SDK 项目中有一些 class 这样的

public interface ISomeSdk
{
    Task<string> GetSomethingFromApiAsync();
}

public class SomeSdk: ISomeSdk
{
    private readonly HttpClient _httpClient;

    public SomeSdk(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<string> GetSomethingFromApiAsync()
    {
        return await _httpClient.GetStringAsync("");
    }
}

你可以很方便地添加一个扩展方法来通过DI注册它(这里是在SDK项目中)

public static class HttpClientExtensions
{
    public static void RegisterSdk(this IServiceCollection services)
    {
        services.AddHttpClient<ISomeSdk, SomeSdk>(client =>
        {
            client.Timeout = TimeSpan.FromSeconds(60);
            client.BaseAddress = new Uri("https://google.com");
        });
    }
}

之后在启动项目中这个class可以添加到DI注册中

public void ConfigureServices(IServiceCollection services)
{
    services.RegisterSdk();
    //...

什么时候需要它就可以使用它

public class SomeController : ControllerBase
{
    private readonly ISomeSdk _someSdk;

    public SomeController(ISomeSdk someSdk)
    {
        _someSdk = someSdk;
    }

更好的方法是使用 HttpClientFactory 创建 HttpClient 实例。

HttpClientFactory 可以创建和管理新的 HttpClient 实例,但在创建新的 HttpClient 实例时,它不会重新创建新的消息处理程序,而是从池中获取一个。然后,它使用该消息处理程序将请求发送到 API。处理程序的默认生命周期设置为两分钟,在此期间,对新 HttpClient 的任何请求都可以重用现有的消息处理程序和连接。这意味着我们不必为每个请求创建一个新的消息处理程序,也不必打开一个新连接,从而防止套接字耗尽问题。

有了 HttpClientHandler,我们可以集中我们的 HttpClient 配置。如果您使用 HttpClientHandler 在每个服务 class 中重复相同的配置,则可以防止这种情况发生。

您可以在此处找到如何使用它的示例 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0