I have a class AuthorizationHandler
that is derived from System.Net.Http.DelegatingHandler
. It has a DI constuctor and overrides the SendAsync
method to retrieve an authorization token from AuthenticationService
before every request.
public class AuthorizationHandler : DelegatingHandler
{
private readonly IAuthenticationService _service;
public AuthorizationHandler(IAuthenticationService service)
{
_service = service;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Authorization =
new AuthenticationHeaderValue("Authorization", await _service.GetAccessToken());
return await base.SendAsync(request, cancellationToken);
}
}
However I receive the following DI registration issue whenever IHttpClientFactory.CreateClient()
is invoked:
System.InvalidOperationException: No service for type 'MyProject.AuthenticationService.AuthorizationHandler' has been registered.
The following is a relevant snippet of my Autofac configuration.
...
// Creates an IoC container and registers infrastructure modules
var containerBuilder = new ContainerBuilder();
...
containerBuilder.RegisterType<AuthorizationHandler>().InstancePerDependency();
// ALSO TRIED
// containerBuilder.RegisterType<AuthorizationHandler>().As<DelegatingHandler>().InstancePerDependency();
containerBuilder.RegisterType<AuthenticationService>().As<IAuthenticationService>().InstancePerLifetimeScope();
containerBuilder.Register<IHttpClientFactory>(_ =>
{
var services = new ServiceCollection();
services.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://example.com/authenticate");
client.DefaultRequestHeaders.TryAddWithoutValidation("x-api-key",
"MY-API-KEY");
})
.AddHttpMessageHandler<AuthorizationHandler>();
// ALSO TRIED
// .AddHttpMessageHandler((provider) => provider.GetRequiredService<AuthorizationHandler>());
var provider = services.BuildServiceProvider();
return provider.GetRequiredService<IHttpClientFactory>();
}).SingleInstance();
It appears you fell victim to one of the classic blunders. The most famous is never get involved in a land war in Asia. But only slightly less well known is this: You can't mix-and-match Autofac registrations with Microsoft.Extensions.DependencyInjection registrations.
I mean, I get it, it would not be awesome to have to replicate some of that .AddHttpClient
stuff. But I also see in the sample snippet this:
containerBuilder.Register<IHttpClientFactory>(_ =>
{
services.AddHttpClient("MyApi", client =>
Which means this is a closure over services
, which gets... temporarily built? and then still ends up contributing to a different container (Autofac), which will then back the DI for the rest of the calls?
Based on the snippet I have to assume you've integrated Autofac as the backing DI container and, as part of that, are using the AutofacServiceProviderFactory
at app startup (as noted in the docs). If you're not doing that, and/or not following the docs, then the rest of the container setup - how you're actually building it, where services
gets created, etc. - is all pretty relevant to the issue at hand and is not part of the question/repro. (That's why minimal reproducible example is so important. What's here is not reproducible and is a step past minimal.)
Making the assumption that you're setting things up in a sort of supported way, the general order of operations in a standard ASP.NET Core app is:
IServiceCollection services
.ConfigureServices(services)
to get registrations into the Microsoft format.AutofacServiceProviderFactory
to get a ContainerBuilder
. This will...
ContainerBuilder
.ContainerBuilder
. (builder.Populate(services)
)ContainerBuilder
with all the Microsoft registrations already in it.ConfigureContainer(builder)
with that ContainerBuilder
.ContainerBuilder
back to AutofacServiceProviderFactory
to...
builder.Build()
.IServiceProvider
interface.So if you have interesting/odd stuff like "mix and match Microsoft registrations inside a delegate that you're registering in Autofac," you're going to get really painful-to-troubleshoot problems like you're describing now. You're kind of working with two different containers, right? You have
services
before ConfigureContainer
ran along with the stuff from ConfigureContainer
; ANDConfigureServices
and the stuff from inside the delegate, but is not the same as the set of registrations being used to back the rest of the application.Don't mix and match. Unwind all that and do not register new services or make tiny temporary containers inside delegates.
I can't promise this will fix the problem, but it will probably make it far, far easier to troubleshoot.