Почему использование HTTP клиентов внутри одного проекта считается не очень практичным
Что проверяют
Знаете ли вы жизненный цикл HttpClient в .NET и понимаете ли, почему у него нетипичные для IDisposable правила. Заодно смотрят, различаете ли вы проблему ресурсов и проблему архитектуры.
Как отвечать
Уточните, о чём вопрос: обычно про то, что по проекту разбросаны свои экземпляры клиента, у каждого модуля свой, со своими заголовками и таймаутами.
По сути. HttpClient реализует IDisposable, и это сбивает с толку: обёрнутый в using на каждый вызов, он оставляет соединения в состоянии ожидания закрытия, и под нагрузкой приложение упирается в нехватку свободных портов. Обратная крайность, один статический экземпляр на весь процесс, чинит сокеты, но фиксирует результат разрешения имени: после переезда сервиса на другой адрес клиент продолжает ходить на старый.
Рабочий ответ это IHttpClientFactory: регистрация через AddHttpClient, типизированный клиент на каждый внешний сервис, пул обработчиков с ограниченным временем жизни, который и переиспользует соединения, и обновляет адрес. Туда же навешивается общее: базовый адрес, заголовки, политики повторов и размыкателя цепи через делегирующие обработчики. При ручном управлении тот же эффект даёт SocketsHttpHandler с заданным PooledConnectionLifetime.
Отдельно про архитектуру: если по HTTP общаются части одного развёртываемого приложения, это лишняя сериализация и новый источник отказов, такие вызовы заменяются обычным вызовом метода.
Где спотыкаются
Называют HttpClient потокобезопасным и на этом останавливаются. Безопасна отправка запросов, а менять BaseAddress, Timeout или DefaultRequestHeaders у общего экземпляра после первого запроса нельзя, именно так и получают чужие заголовки в своём вызове.
Второе: считают, что фабрика решает всё. Типизированный клиент живёт недолго, а обработчик под ним переживает его, поэтому состояние, которое должно сохраняться между запросами, в клиенте хранить нельзя.
Третье: заворачивают вызов в перехват исключений и глушат его. Клиент обязан различать таймаут, отказ соединения и код ответа: EnsureSuccessStatusCode превращает ответ в исключение, и повторять запрос имеет смысл далеко не на каждом коде.