#c# #.net-core #asp.net-core-webapi #webapi #httpclientfactory
Вопрос:
Введение
Я разрабатываю SDK (это просто оболочка для REST API). Этот SDK внутренне использует HttpClient для выполнения запросов к API. SDK ориентирован на стандарт .NET 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(...);
}
}
Вопрос
SDK используется другими разработчиками в ASP.NET Основные проекты. Мне известно о проблемах с HttpClient, и я знаю, что MS DOCS рекомендует вводить HttpClientFactory, чтобы избежать исключения SocketException и отразить изменения DNS.
Первое решение, которое приходит на ум, — позволить пользователю передать HttpClient (то есть из HttpClientFactory) в конструкторе SDK или другими методами. Подобный этому.
public async Task<string> GetSomethingFromApiAsync(HttpClient clientFromFactory)
{
return await clientFromFactory.GetStringAsync(...);
}
Однако существует проблема, связанная с тем, что SDK настраивает HttpClient перед его использованием (прокси, тайм-аут и т.д.). Поэтому его инициализацию следует оставить в конструкторе SDK.
Вопрос
Мое текущее решение состоит в том, чтобы внедрить SDK в качестве синглтона в ASP.NET Основные проекты и примите проблемы отражения DNS. Я все еще хотел бы спросить, есть ли кто-нибудь, кто порекомендовал бы мне лучший подход к этой проблеме.
Большое тебе спасибо, Адам.
Ответ №1:
Лучший способ-использовать HttpClientFactory для создания экземпляров HttpClient.
HttpClientFactory может создавать новые экземпляры HttpClient и управлять ими, но также при создании новых экземпляров HttpClient он не создает новый обработчик сообщений, а берет его из пула. Затем он использует этот обработчик сообщений для отправки запросов в API. Время жизни обработчика по умолчанию равно двум минутам, и в течение этого времени любой запрос на новый HttpClient может повторно использовать существующий обработчик сообщений и соединение. Это означает, что нам не нужно создавать новый обработчик сообщений для каждого запроса, а также не нужно открывать новое соединение, что предотвращает проблему исчерпания сокета.
С помощью HttpClientHandler мы можем централизовать конфигурацию нашего HttpClient. Если вы повторите одну и ту же конфигурацию в каждом классе службы с помощью HttpClientHandler, вы можете предотвратить это.
Примеры того, как его использовать, вы можете найти здесь https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0
Ответ №2:
Лучший способ создать экземпляр HttpClient-использовать DI.
Итак, предположим, что в вашем проекте SDK есть такой класс
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");
});
}
}
после этого в стартовом проекте этот класс может быть добавлен при регистрации в DI
public void ConfigureServices(IServiceCollection services)
{
services.RegisterSdk();
//...
Что позволяет использовать его всякий раз, когда это необходимо
public class SomeController : ControllerBase
{
private readonly ISomeSdk _someSdk;
public SomeController(ISomeSdk someSdk)
{
_someSdk = someSdk;
}