IHttpClientFactory с .NET
В этой статье вы узнаете, как использовать IHttpClientFactory
интерфейс для создания HttpClient
типов с различными основами .NET, такими как внедрение зависимостей (DI), ведение журнала и конфигурация. Тип HttpClient впервые появился в .NET Framework 4.5 в 2012 году. Иными словами, он используется уже довольно давно. HttpClient
используется для выполнения HTTP-запросов и обработки ответов HTTP из веб-ресурсов, определенных Uri. При передаче интернет-трафика в большинстве случаев используется протокол HTTP.
В соответствии с современными принципами разработки приложений, основанными на лучших методиках, класс IHttpClientFactory выступает в качестве уровня абстракции для фабрики, который может создавать экземпляры HttpClient
с настраиваемыми конфигурациями. Тип IHttpClientFactory впервые появился в .NET Core 2.1. В распространенных рабочих нагрузках .NET на основе HTTP можно воспользоваться преимуществами ПО промежуточного слоя для обработки устойчивых и временных сбоев.
Примечание.
Если вашему приложению требуются файлы cookie, лучше избежать использования IHttpClientFactory в приложении. Альтернативные способы управления клиентами см . в рекомендациях по использованию HTTP-клиентов.
Внимание
Управление HttpClient
временем существования экземпляров, созданных IHttpClientFactory
вручную, отличается от экземпляров, созданных вручную. Стратегии предназначены для использования кратковременных клиентов, созданных IHttpClientFactory
или долгосрочными клиентами с PooledConnectionLifetime
настройкой. Дополнительные сведения см. в разделе "Управление временем существования HttpClient" и "Рекомендации по использованию HTTP-клиентов".
Тип IHttpClientFactory
.
Для всех примеров исходного кода, предоставленного Microsoft.Extensions.Http
в этой статье, требуется установка пакета NuGet. Кроме того, в примерах кода демонстрируется использование HTTP-запросов GET
для получения пользовательских Todo
объектов из бесплатного API заполнителя {JSON}.
При вызове любого из методов расширения AddHttpClient вы добавляете IHttpClientFactory
и связанные службы в IServiceCollection. Тип IHttpClientFactory
предоставляет следующие преимущества:
- Предоставление класса
HttpClient
в качестве типа, готового к внедрению зависимостей. - Центральное расположение для именования и настройки логических экземпляров
HttpClient
. - Кодификация концепции исходящего ПО промежуточного слоя путем делегирования обработчиков в
HttpClient
. - Предоставление методов расширений для ПО промежуточного слоя на основе Polly для делегирования обработчиков в
HttpClient
. - Управляет кэшированием и временем существования базовых HttpClientHandler экземпляров. Автоматическое управление позволяет избежать обычных проблем со службой доменных имен (DNS), которые возникают при управлении временем существования
HttpClient
вручную. - Настройка параметров ведения журнала (через ILogger) для всех запросов, отправленных через клиентов, созданных фабрикой.
Шаблоны потребления
Существует несколько способов использования IHttpClientFactory
в приложении:
Оптимальный подход зависит от требований приложения.
Базовое использование
Чтобы зарегистрировать IHttpClientFactory
, вызовите AddHttpClient
:
using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();
using IHost host = builder.Build();
Использование служб может потребовать IHttpClientFactory
в качестве параметра конструктора с внедрением зависимостей. Следующий код использует IHttpClientFactory
для создания экземпляра HttpClient
:
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace BasicHttp.Example;
public sealed class TodoService(
IHttpClientFactory httpClientFactory,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
using HttpClient client = httpClientFactory.CreateClient();
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo types
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"https://jsonplaceholder.typicode.com/todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
Подобное использование IHttpClientFactory
— это хороший способ рефакторинга имеющегося приложения. Он не оказывает влияния на использование HttpClient
. Там, где в существующем приложении создаются экземпляры HttpClient
, используйте вызовы к CreateClient.
Именованные клиенты
Именованные клиенты являются хорошим выбором в следующих случаях:
- Приложение требует много отдельных использований
HttpClient
. - Многие
HttpClient
экземпляры имеют разные конфигурации.
Конфигурацию именованного HttpClient
можно указать во время регистрации в IServiceCollection
:
using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);
builder.Services.AddHttpClient(
httpClientName,
client =>
{
// Set the base address of the named client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
В приведенном выше коде клиент регистрируется со следующими сведениями:
- именем, извлеченным из конфигурации в
"TodoHttpClientName"
; - базовым адресом
https://jsonplaceholder.typicode.com/
; "User-Agent"
.
Вы можете использовать конфигурацию для указания имен HTTP-клиентов. Это помогает избежать ошибок в именах клиентов при их добавлении и создании. В этом примере для настройки имени HTTP-клиента используется файл appsettings.json:
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
Вы можете легко расширить эту конфигурацию и сохранить дополнительные сведения о том, как будет работать клиент HTTP. Дополнительные сведения см. в статье Конфигурация в .NET.
Создание клиента
При каждом вызове CreateClient:
- создается новый экземпляр
HttpClient
; - вызывается действие настройки.
Чтобы создать именованный клиент, передайте его имя в CreateClient
:
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;
namespace NamedHttp.Example;
public sealed class TodoService
{
private readonly IHttpClientFactory _httpClientFactory = null!;
private readonly IConfiguration _configuration = null!;
private readonly ILogger<TodoService> _logger = null!;
public TodoService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
ILogger<TodoService> logger) =>
(_httpClientFactory, _configuration, _logger) =
(httpClientFactory, configuration, logger);
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
string? httpClientName = _configuration["TodoHttpClientName"];
using HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
_logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
В приведенном выше коде в HTTP-запросе не требуется указывать имя узла. Достаточно передать только путь, так как используется базовый адрес, заданный для клиента.
Типизированные клиенты
Типизированные клиенты:
- предоставляют те же возможности, что и именованные клиенты, без необходимости использовать строки в качестве ключей.
- Это помогает IntelliSense и компилятору при использовании клиентов.
- Они предоставляют единое расположение для настройки и взаимодействия с конкретным клиентом
HttpClient
. Например, можно использовать один типизированный клиент:- для одной серверной конечной точки;
- для инкапсуляции всей логики, связанной с конечной точкой.
- Поддерживаются работа с внедрением зависимостей и возможность вставки в нужное место в приложении.
Типизированный клиент принимает параметр HttpClient
в конструкторе:
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace TypedHttp.Example;
public sealed class TodoService(
HttpClient httpClient,
ILogger<TodoService> logger) : IDisposable
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
public void Dispose() => httpClient?.Dispose();
}
В предыдущем коде:
- Конфигурация задается при добавлении типизированного клиента в коллекцию служб.
HttpClient
назначается как переменная (поле) с областью видимости класса и используется с предоставляемыми API.
Можно создать связанные с API методы, которые предоставляют функциональные возможности HttpClient
. Например, GetUserTodosAsync
метод инкапсулирует код для извлечения объектов, относящихся Todo
к пользователю.
Следующий код вызывает AddHttpClient регистрацию типизированного клиентского класса:
using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient<TodoService>(
client =>
{
// Set the base address of the typed client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
Типизированный клиент регистрируется во внедрении зависимостей как временный. В приведенном выше коде AddHttpClient
регистрирует TodoService
как временную службу. Эта регистрация использует фабричный метод для следующих задач:
- Создайте экземпляр
HttpClient
. - Создайте экземпляр
TodoService
, передав его конструктору экземплярHttpClient
.
Внимание
Использование типизированных клиентов в одноэлементных службах может быть опасным. Дополнительные сведения см . в разделе "Избегание типизированных клиентов" в разделе " Службы с одним типом".
Примечание.
При регистрации типизированного клиента с AddHttpClient<TClient>
помощью метода тип должен иметь конструктор, TClient
который принимает в HttpClient
качестве параметра. Кроме того, TClient
тип не должен быть зарегистрирован в контейнере DI отдельно, так как это приведет к последующей регистрации перезаписи бывшего.
Созданные клиенты
IHttpClientFactory
можно использовать в сочетании с библиотеками сторонних разработчиков, например Refit. Refit — это библиотека REST для .NET. Она поддерживает декларативные определения REST API, сопоставляя методы интерфейса с конечными точками. Реализация интерфейса формируется динамически с помощью RestService
с использованием HttpClient
для совершения внешних вызовов HTTP.
Рассмотрим следующий record
тип:
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
В следующем примере, который является простым интерфейсом, используется пакет NuGet Refit.HttpClientFactory
:
using Refit;
using Shared;
namespace GeneratedHttp.Example;
public interface ITodoService
{
[Get("/todos?userId={userId}")]
Task<Todo[]> GetUserTodosAsync(int userId);
}
Предыдущий интерфейс C#:
- Определяет метод с именем
GetUserTodosAsync
, который возвращает экземплярTask<Todo[]>
. - Объявляет для внешнего API атрибут
Refit.GetAttribute
с путем и строкой запроса.
Можно добавить типизированный клиент, используя Refit для создания реализации:
using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddRefitClient<ITodoService>()
.ConfigureHttpClient(client =>
{
// Set the base address of the named client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
При необходимости можно использовать определенный интерфейс с реализацией, предоставленной с помощью внедрения зависимостей и Refit.
Выполнение запросов POST, PUT и DELETE
В предыдущих примерах все HTTP-запросы используют GET
http-команду. HttpClient
также поддерживает другие HTTP-команды, в том числе:
POST
PUT
DELETE
PATCH
Полный список поддерживаемых HTTP-команд см. в статье HttpMethod. Дополнительные сведения о выполнении HTTP-запросов см. в статье "Отправка запроса с помощью HttpClient".
В следующем примере показано, как выполнить HTTP-запрос POST
:
public async Task CreateItemAsync(Item item)
{
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();
}
В приведенном выше коде метод CreateItemAsync
выполняет следующие задачи:
- сериализует параметр
Item
в JSON с помощьюSystem.Text.Json
. Для настройки процесса сериализации используется экземпляр JsonSerializerOptions. - создает экземпляр StringContent для упаковки сериализованного JSON для отправки в тексте HTTP-запроса.
- вызывает метод PostAsync для отправки содержимого JSON по указанному URL-адресу. Это относительный URL-адрес, который добавляется в свойство HttpClient.BaseAddress.
- вызывает метод EnsureSuccessStatusCode, чтобы создавать исключение, если код состояния ответа означает неудачное выполнение.
HttpClient
также поддерживает другие типы содержимого. Например, MultipartContent и StreamContent. Полный список поддерживаемого содержимого см. в статье HttpContent.
В следующем примере показан HTTP-запрос PUT
:
public async Task UpdateItemAsync(Item item)
{
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();
}
Приведенный выше код очень похож на POST
пример. Метод UpdateItemAsync
вызывает PutAsync вместо PostAsync
.
В следующем примере показан HTTP-запрос DELETE
:
public async Task DeleteItemAsync(Guid id)
{
using HttpResponseMessage httpResponse =
await httpClient.DeleteAsync($"/api/items/{id}");
httpResponse.EnsureSuccessStatusCode();
}
В приведенном выше коде метод DeleteItemAsync
вызывает DeleteAsync. Поскольку HTTP-запросы DELETE обычно не содержат текст, метод DeleteAsync
не предоставляет перегрузку, которая принимает экземпляр HttpContent
.
Дополнительные сведения об использовании различных HTTP-команд с HttpClient
см. в статье HttpClient.
Управление жизненным циклом HttpClient
При каждом вызове CreateClient
в IHttpClientFactory
возвращается новый экземпляр HttpClient
. Один HttpClientHandler экземпляр создается на имя клиента. Фабрика обеспечивает управление временем существования экземпляров HttpClientHandler
.
IHttpClientFactory
кэширует HttpClientHandler
экземпляры, созданные фабрикой для уменьшения потребления ресурсов. Экземпляр HttpClientHandler
может быть повторно использован из кэша при создании нового HttpClient
экземпляра, если срок его существования не истек.
Кэширование обработчиков желательно, так как каждый обработчик обычно управляет собственным базовым пулом http-подключений. Создание дополнительных обработчиков может привести к нехватке сокета и задержкам подключения. Некоторые обработчики поддерживают подключения открытыми в течение неопределенного периода, что может помешать обработчику отреагировать на изменения DNS.
Время существования обработчика по умолчанию — две минуты. Чтобы переопределить значение по умолчанию, вызовите для каждого клиента SetHandlerLifetime в IServiceCollection
:
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
Внимание
HttpClient
экземпляры, созданные с помощью IHttpClientFactory
, предназначены для кратковременной жизни.
Повторное использование и повторное
HttpMessageHandler
восстановление после истечения срока их существования является важным дляIHttpClientFactory
обеспечения реагирования обработчиков на изменения DNS.HttpClient
привязан к конкретному экземпляру обработчика при его создании, поэтому новыеHttpClient
экземпляры должны быть своевременно запрошены, чтобы клиент получил обновленный обработчик.Удаление таких
HttpClient
экземпляров, созданных фабрикой, не приведет к исчерпанию сокета, так как его удаление не приведет к удалениюHttpMessageHandler
.IHttpClientFactory
отслеживает и удаляет ресурсы, используемые для созданияHttpClient
экземпляров, в частностиHttpMessageHandler
экземпляров, как только срок их существования истекает, и они больше неHttpClient
используются.
Сохранение одного HttpClient
экземпляра в живых в течение длительного времени является общим шаблоном, который можно использовать в качестве альтернативыIHttpClientFactory
, однако для этого шаблона требуется дополнительная настройка, напримерPooledConnectionLifetime
. Вы можете использовать либо долгоживущие клиенты с PooledConnectionLifetime
клиентами, созданными в течение длительного времени, либо краткосрочными IHttpClientFactory
клиентами. Сведения о том, какую стратегию следует использовать в приложении, см. в рекомендациях по использованию HTTP-клиентов.
Настройка HttpMessageHandler
Иногда необходимо контролировать конфигурацию внутреннего обработчика HttpMessageHandler, используемого клиентом.
При добавлении именованного или типизированного клиента возвращается IHttpClientBuilder. Для определения делегата в IServiceCollection
можно использовать метод расширения ConfigurePrimaryHttpMessageHandler. Делегат используется для создания и настройки основного обработчика HttpMessageHandler
, используемого этим клиентом:
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
Настройка HttClientHandler
позволяет указать прокси-сервер для HttpClient
экземпляра среди различных других свойств обработчика. Дополнительные сведения см. в разделе "Прокси-сервер для каждого клиента".
Дополнительная настройка
Существует несколько дополнительных вариантов настройки для управления IHttpClientHandler
:
Метод | Description |
---|---|
AddHttpMessageHandler | Добавляет дополнительный обработчик сообщений для именованного объекта HttpClient . |
AddTypedClient | Настраивает привязку между TClient и именованным объектом HttpClient , связанным с IHttpClientBuilder . |
ConfigureHttpClient | Добавляет делегат, который будет использоваться для настройки именованного HttpClient . |
ConfigurePrimaryHttpMessageHandler | Настраивает основной обработчик сообщений HttpMessageHandler из контейнера внедрения зависимостей для именованного объекта HttpClient . |
RedactLoggedHeaders | Задает коллекцию имен заголовков HTTP, для которых значения должны быть исправлены перед записью в журнал. |
SetHandlerLifetime | Задает период времени, в течение которого экземпляр HttpMessageHandler может использоваться повторно. Для каждого именованного клиента можно указать свое значение времени существования настроенного обработчика. |
UseSocketsHttpHandler | Настраивает новый или ранее добавленный SocketsHttpHandler экземпляр из контейнера внедрения зависимостей, который будет использоваться в качестве основного обработчика именованного HttpClient . (только .NET 5+ ) |
Использование IHttpClientFactory вместе с SocketsHttpHandler
Реализация SocketsHttpHandler
добавлена HttpMessageHandler
в .NET Core 2.1, которая позволяет PooledConnectionLifetime
настроить. Этот параметр используется для обеспечения реагирования обработчика на изменения DNS, поэтому использование SocketsHttpHandler
считается альтернативой использованию IHttpClientFactory
. Дополнительные сведения см. в руководстве по использованию HTTP-клиентов.
SocketsHttpHandler
Однако вместе IHttpClientFactory
можно использовать и улучшить настройку. Используя оба этих API, можно воспользоваться возможностью настройки на низком уровне (например, при LocalCertificateSelectionCallback
использовании динамического выбора сертификатов) и на высоком уровне (например, с использованием интеграции DI и нескольких конфигураций клиента).
Чтобы использовать оба API:
- Укажите
SocketsHttpHandler
какPrimaryHandler
через ConfigurePrimaryHttpMessageHandlerили UseSocketsHttpHandler (только .NET 5+). - Настройка SocketsHttpHandler.PooledConnectionLifetime на основе интервала, в который требуется обновить DNS; например, до значения, которое было ранее в
HandlerLifetime
. - (Необязательно) Так как
SocketsHttpHandler
будет обрабатывать пул подключений и переработку, обработчик повторно используется наIHttpClientFactory
уровне больше не требуется. Ее можно отключить, задав для параметраHandlerLifetime
значениеTimeout.InfiniteTimeSpan
.
services.AddHttpClient(name)
.UseSocketsHttpHandler((handler, _) =>
handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
.SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime
В приведенном выше примере 2 минуты были выбраны произвольно в целях иллюстрации, выравнивая значение по умолчанию HandlerLifetime
. Вы должны выбрать значение на основе ожидаемой частоты ИЗМЕНЕНИЙ DNS или других сетевых изменений. Дополнительные сведения см . в разделе "Поведение DNS" в HttpClient
рекомендациях и разделе "Примечания" в PooledConnectionLifetime документации по API.
Избегайте типизированных клиентов в одноэлементных службах
При использовании именованного подхода IHttpClientFactory
клиента внедряется в службы, а HttpClient
экземпляры создаются при каждом вызове HttpClient
CreateClient при каждом необходимости.
Однако с типизированным подходом клиента типизированные клиенты являются временными объектами, которые обычно внедряются в службы. Это может привести к проблеме, так как типизированный клиент может быть внедрен в одну службу.
Внимание
Ожидается, что типизированные клиенты будут короткими в том же смысле, что HttpClient
и экземпляры, созданные IHttpClientFactory
(дополнительные сведения см. в разделеHttpClient
"Управление временем существования"). После создания IHttpClientFactory
типизированного экземпляра клиента он не контролирует. Если типизированный экземпляр клиента фиксируется в одном элементе, он может предотвратить реагирование на изменения DNS, победив одно из целей IHttpClientFactory
.
Если необходимо использовать HttpClient
экземпляры в одной службе, рассмотрите следующие варианты:
- Вместо этого используйте именованный подход клиента , внедряя
IHttpClientFactory
одноэлементную службу и повторно создаваяHttpClient
экземпляры при необходимости. - Если требуется типизированный подход клиента , используйте
SocketsHttpHandler
егоPooledConnectionLifetime
в качестве основного обработчика. Дополнительные сведения об использованииSocketsHttpHandler
IHttpClientFactory
с помощью см. в разделе "Использование IHttpClientFactory вместе с SocketsHttpHandler".
Области обработчика сообщений в IHttpClientFactory
IHttpClientFactory
создает отдельную область DI для каждого HttpMessageHandler
экземпляра. Эти области di отделены от областей di приложения (например, ASP.NET области входящих запросов или созданной пользователем области DI), поэтому они не будут совместно использовать экземпляры служб с областью действия. Области обработчика сообщений привязаны к времени существования обработчика и могут выходить за пределы областей приложений, что может привести к повторному использовании одного HttpMessageHandler
экземпляра с одинаковыми внедренными зависимостями области между несколькими входящими запросами.
Пользователям настоятельно рекомендуется не кэшировать сведения , связанные с областью действия (например, данные из HttpContext
) в HttpMessageHandler
экземплярах и использовать зависимости с областью действия с осторожностью, чтобы избежать утечки конфиденциальной информации.
Если вам требуется доступ к области di приложения из обработчика сообщений, для проверки подлинности в качестве примера вы инкапсулируете логику с поддержкой областей в отдельном DelegatingHandler
временном режиме и заключите его вокруг HttpMessageHandler
экземпляра из кэша IHttpClientFactory
. Чтобы получить доступ к вызову IHttpMessageHandlerFactory.CreateHandler обработчика для любого зарегистрированного именованного клиента. В этом случае вы создадите HttpClient
экземпляр самостоятельно с помощью созданного обработчика.
В следующем примере показано создание HttpClient
с учетом DelegatingHandler
области:
if (scopeAwareHandlerType != null)
{
if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
{
throw new ArgumentException($"""
Scope aware HttpHandler {scopeAwareHandlerType.Name} should
be assignable to DelegatingHandler
""");
}
// Create top-most delegating handler with scoped dependencies
scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
if (scopeAwareHandler.InnerHandler != null)
{
throw new ArgumentException($"""
Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
Scope aware HttpHandler should be registered as Transient.
""");
}
}
// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);
if (scopeAwareHandler != null)
{
scopeAwareHandler.InnerHandler = handler;
handler = scopeAwareHandler;
}
HttpClient client = new(handler);
Дополнительное обходное решение может выполняться с помощью метода расширения для регистрации параметров с учетом DelegatingHandler
области и переопределения регистрации по умолчанию IHttpClientFactory
временной службой с доступом к текущей области приложения:
public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
builder.Services.TryAddTransient<THandler>();
if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
{
// Override default IHttpClientFactory registration
builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
}
builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
builder.Name, options => options.HttpHandlerType = typeof(THandler));
return builder;
}
Дополнительные сведения см. в полном примере.
См. также
- Распространенные
IHttpClientFactory
проблемы с использованием - Внедрение зависимостей в .NET
- Ведение журнала в .NET
- Конфигурация в .NET
- IHttpClientFactory
- IHttpMessageHandlerFactory
- HttpClient
- Выполнение HTTP-запросов с помощью HttpClient
- Реализация повторных попыток вызова HTTP с экспоненциальной задержкой