Reputation: 2740
In Blazor I have setup two HttpClients. One for my API and one for MS Graph API. The Graph API is new, and have forced me to find a way to inject a named httpclient in to my services.
This is all the code in Main
public class Program
{
public static async Task Main(string[] args)
{
var b = WebAssemblyHostBuilder.CreateDefault(args);
b.RootComponents.Add<App>("app");
var samsonApiUrl = new Uri(b.HostEnvironment.BaseAddress + "api/");
b.Services.AddHttpClient("SamsonApi",client =>
{
client.BaseAddress = samsonApiUrl;
// add jwt token to header
// add user agent to header
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
b.Services.AddTransient<GraphCustomAuthorizationMessageHandler>();
b.Services.AddHttpClient<GraphHttpClientService>("GraphAPI",
client => client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphCustomAuthorizationMessageHandler>();
b.Services.AddScoped(provider => provider.GetService<IHttpClientFactory>().CreateClient("SamsonApi"));
b.Services.AddScoped(provider => provider.GetService<IHttpClientFactory>().CreateClient("GraphAPI"));
b.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
{
b.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("1c8d4e31-97dd-4a54-8c2b-0d81e4356bf9/API.Access");
options.UserOptions.RoleClaim = "role";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomUserFactory>();
// add Radzen services
b.Services.AddScoped<DialogService>();
b.Services.AddScoped<NotificationService>();
b.Services.AddScoped<TooltipService>();
// add samson component services
b.Services.AddSingleton<FormTitleState>();
// Add Http Services
b.Services.Scan(scan =>
{
scan.FromAssemblyOf<ICustomerService>()
.AddClasses(classes => classes.Where(type => type.Name.EndsWith("Service")))
.AsMatchingInterface()
.WithScopedLifetime();
});
await b.Build().RunAsync();
}
}
This is the code that has to change. It's scan all my service and get a HttpClient injected. And since I now have two I get a random client injected. How can I inject a named client into all of my services? I can handle the graph API service as a special case.
b.Services.Scan(scan =>
{
scan.FromAssemblyOf<ICustomerService>()
.AddClasses(classes => classes.Where(type => type.Name.EndsWith("Service")))
.AsMatchingInterface()
.WithScopedLifetime();
});
Example of a service calling my API
public class ActiveAgreementService : IActiveAgreementService
{
private readonly HttpClient _client;
public ActiveAgreementService(HttpClient client)
{
_client = client;
}
public async Task<List<ActiveAgreementDto>> GetActiveAgreements()
{
var lst = await _client.GetFromJsonAsync<ActiveAgreementDto[]>("ActiveAgreement");
return lst.ToList();
}
}
Okay ended up with replacing HttpClient with IHttpClientFactory in all my services
public UserService(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient("SamsonApi");
}
Upvotes: 1
Views: 6298
Reputation: 5259
I assume you're using ASP.NET Core, although it's not clear which dependency injection framework you're using.
In that case, you could have your classes depend on IHttpClientFactory
and then setup the configuration with named clients:
// Named client like you're currently doing
b.Services.AddHttpClient("SamsonApi", client =>
{
client.BaseAddress = samsonApiUrl;
// add jwt token to header
// add user agent to header
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
//...
b.Services.AddHttpClient("GraphAPI", client =>
client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphCustomAuthorizationMessageHandler>();
// And in your dependent class
public class ActiveAgreementService : IActiveAgreementService
{
private readonly HttpClient _client;
public ActiveAgreementService(IHttpClientFactory clientFac)
{
// Whichever one you need:
_client = clientFac.CreateClient("SamsonApi");
_client = clientFac.CreateClient("GraphAPI");
}
public async Task<List<ActiveAgreementDto>> GetActiveAgreements()
{
var lst = await _client.GetFromJsonAsync<ActiveAgreementDto[]>("ActiveAgreement");
return lst.ToList();
}
}
... or with typed clients you specify the instance for each class that depends on it:
// This HttpClient is only injected into ActiveAgreementService
b.Services.AddHttpClient<ActiveAgreementService>(client =>
{
client.BaseAddress = samsonApiUrl;
// add jwt token to header
// add user agent to header
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
//...
// This HttpClient is only injected into GraphHttpClientService
b.Services.AddHttpClient<GraphHttpClientService>(client =>
client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphCustomAuthorizationMessageHandler>();
// And in your dependent class
public class ActiveAgreementService : IActiveAgreementService
{
private readonly HttpClient _client;
public ActiveAgreementService(HttpClient client)
{
_client = client;
}
public async Task<List<ActiveAgreementDto>> GetActiveAgreements()
{
var lst = await _client.GetFromJsonAsync<ActiveAgreementDto[]>("ActiveAgreement");
return lst.ToList();
}
}
Upvotes: 6