我有一个名为
AddHeadersHandler
的 HTTP 消息处理程序,它扩展了 System.Net.Http.DelegatingHandler
,我需要将其添加到所有当前和未来的 HttpClient
实例,包括类型化、命名和非命名客户端。
我知道我可以使用
.AddHttpMessageHandler<AddHeadersHandler>()
为特定客户端添加处理程序,但如何将其添加到所有客户端?
// AddHeadersHandler.cs
public class AddHeadersHandler: DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.TryAddWithoutValidation("X-Correlation-Id", Guid.NewGuid.ToString());
return base.SendAsync(request, cancellationToken);
}
}
// Startup.cs
services
.AddHttpContextAccessor()
.AddTransient<AddHeadersHandler>();
services
.AddHttpClient<MyClient>()
.AddHttpMessageHandler<AddHeadersHandler>(); // I don't want to specify this for each client.
// MyClient.cs
public class MyClient
{
public HttpClient HttpClient { get; }
public MyClient(HttpClient httpClient)
{
HttpClient = httpClient;
}
public async Task GetTest()
{
await HttpClient.GetAsync("https://localhost:5001/test"); // This should have headers attached.
}
}
可以通过为
所有命名选项配置
HttpClientFactoryOptions
来完成。我们需要在 HttpMessageHandlerBuilderActions
中提供一个委托,它将包含您的处理程序到 AdditionalHandlers
属性列表。
使用 Options 模式有多种方法可以做到这一点。
如果您的处理程序有任何依赖项(例如
IHttpContextAccessor
获取当前相关 ID),我们希望使用依赖项注入来解决它。
我们可以使用 OptionsBuilder API 通过依赖注入来获取所需的处理程序。不幸的是,OptionsBuilder API 没有提供像 .ConfigureAll
那样为所有命名
实例配置选项的方法。
幸运的是,我们可以通过为
IConfigureOptions<HttpClientFactoryOptions>
注册一个工厂方法来获得我们需要的东西,如下所示:
// Startup.cs
services.AddSingleton<IConfigureOptions<HttpClientFactoryOptions>>(provider =>
{
// When name is null, it will be used for all configurations.
return new ConfigureNamedOptions<HttpClientFactoryOptions>(name: null, options =>
{
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
// Here we have access to ServiceProvider to get an instance of the handler.
builder.AdditionalHandlers.Add(provider.GetRequiredService<AddHeadersHandler>());
});
});
});
以下改进的答案受到LostInComputer的启发。
在
.ConfigureAll
中添加 Startup.cs
并通过构建器对象使用 IServiceProvider
,如下所示:
services.ConfigureAll<HttpClientFactoryOptions>(options =>
{
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
builder.AdditionalHandlers.Add(builder.Services.GetRequiredService<AddHeadersHandler>());
});
});
我不相信上面概述的方法,当我在常规应用程序(不是 Azure Function App)上尝试它时,我遇到了这个问题中概述的确切问题:无法访问已处置对象。 所以我认为应该配置这个ConfigureAll方法来为每个客户端创建一个新的处理程序。