[英]How to inject access token to HttpClient in Service registration in Blazor server app?
I have Blazor server app to use Identity Server 4 for authentication and authorization purposes.我有 Blazor 服务器应用程序来使用 Identity Server 4 进行身份验证和授权。 And, I have a protected api (JWT token) to provide data to Blazor server app.
而且,我有一个受保护的 api(JWT 令牌)来向 Blazor 服务器应用程序提供数据。
I have followed this post to get access token and pass it to HttpClient
during service registration as below,我已经按照这篇文章获取访问令牌并在服务注册期间将其传递给
HttpClient
,如下所示,
Startup.cs启动.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAntDesign();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<TokenProvider>(); <--
ApplicationInitializer.Initialize(Configuration, services);
}
ApplicationInitializer.cs应用程序初始化器.cs
public static class ApplicationInitializer
{
public static void Initialize(IConfiguration configuration, IServiceCollection services)
{
var installers = typeof(Startup).Assembly.ExportedTypes
.Where(w => typeof(IInstaller).IsAssignableFrom(w) && !w.IsInterface && !w.IsAbstract)
.Select(Activator.CreateInstance)
.Cast<IInstaller>()
.ToList();
installers.ForEach(installer => installer.InstallServices(services, configuration));
}
}
ServiceCollectionRegistration.cs ServiceCollectionRegistration.cs
public class ServiceCollectionRegistration : IInstaller
{
public void InstallServices(IServiceCollection services, IConfiguration configuration)
{
//Register api (NSwag generated) clients with HttpClient from HttpClientFactory
var apiOptions = new ApiOptions();
configuration.GetSection(nameof(ApiOptions)).Bind(apiOptions);
services.AddHttpClient("api", (provider, client) =>
{
client.BaseAddress = new Uri(apiOptions.ApiUrl);
//This is not working as expected. Access Token is always null
var tokenProvider = services.BuildServiceProvider().GetRequiredService<TokenProvider>();
var accessToken = tokenProvider.AccessToken;
if (accessToken != null)
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
});
var asm = Assembly.GetExecutingAssembly();
var interfaces = asm.GetInterfaces();
foreach (var interfaceType in interfaces.Where(x => x.Name.EndsWith("Client")))
{
var currentInterfaceType = interfaceType;
var implementations = asm.GetImplementationsOfInterface(interfaceType);
implementations.ToList().ForEach(i =>
{
services.AddScoped(currentInterfaceType, ctx =>
{
var clientFactory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();
var httpClient = clientFactory.CreateClient("api");
return Activator.CreateInstance(i, httpClient);
});
});
}
//Register all provider type to their interface type using reflection
foreach (var interfaceType in interfaces.Where(x => !x.Name.EndsWith("Client")))
{
var currentInterfaceType = interfaceType;
var implementations = asm.GetImplementationsOfInterface(interfaceType);
if (implementations.Count > 1)
implementations.ToList().ForEach(i => services.AddScoped(currentInterfaceType, i));
else
implementations.ToList().ForEach(i => services.AddScoped(currentInterfaceType, i));
}
new AutoMapperConfiguration().RegisterProfiles(services);
}
}
I am unable to assign access token to HttpClient
object since it was null always.我无法将访问令牌分配给
HttpClient
object,因为它始终是 null。 Did I miss anything?我错过了什么吗?
I am unable to assign access token to HttpClient object since it was null always.
我无法将访问令牌分配给 HttpClient object,因为它始终是 null。
Since the token is to be accessed from the current HttpContext
during a request, it wont be available at the time when registering the client.由于令牌是在请求期间从当前
HttpContext
访问的,因此在注册客户端时将不可用。
This makes trying to add a default header not ideal.这使得尝试添加默认 header 并不理想。
Consider changing the approach.考虑改变方法。
Create a message handler to intercept/extract and inject the desired header during the scope of a request创建消息处理程序以在请求的 scope 期间拦截/提取并注入所需的 header
public class TokenHandler : DelegatingHandler {
private readonly IHttpContextAccessor accessor;
public TokenHandler(IHttpContextAccessor accessor) => this.accessor = accessor;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
//get the token
var accessToken = await accessor.HttpContext.GetTokenAsync("access_token");
//add header
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
//continue down stream request
return await base.SendAsync(request, cancellationToken);
}
}
include the message handler in the pipeline when registering the client, like in this simplified example for adding the API named client在注册客户端时在管道中包含消息处理程序,就像在这个简化示例中添加名为客户端的 API
public void ConfigureServices(IServiceCollection services) {
services.AddAntDesign();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHttpContextAccessor();
services.AddScoped<TokenHandler>();
//Register api (NSwag generated) clients with HttpClient from HttpClientFactory
ApiOptions apiOptions = Configuration.GetSection(nameof(ApiOptions)).Get<ApiOptions>();
services
.AddHttpClient("api", (client) => {
client.BaseAddress = new Uri(apiOptions.ApiUrl);
})
.AddHttpMessageHandler<TokenHandler>(); //inject token using our token handler
//...
}
The ServiceCollectionRegistration.InstallServices
is overly complicated and will be difficult to maintain (IMO) with it Separation of Concerns (SoC) violations and improperly calling BuildServiceProvider
that will cause problems later on. ServiceCollectionRegistration.InstallServices
过于复杂,并且难以维护 (IMO),因为它违反了关注点分离 (SoC) 违规和不正确地调用BuildServiceProvider
,这将在以后导致问题。
Use the provider in the factory delegate when registering your types so the proper IServiceProvider
is used to resolve service dependencies.注册类型时使用工厂委托中的提供程序,以便使用正确的
IServiceProvider
来解决服务依赖关系。
//...
foreach (var interfaceType in interfaces.Where(x => x.Name.EndsWith("Client"))) {
var currentInterfaceType = interfaceType;
var implementations = asm.GetImplementationsOfInterface(interfaceType);
implementations.ToList().ForEach(i => {
services.AddScoped(currentInterfaceType, provider => {
var clientFactory = provider.GetRequiredService<IHttpClientFactory>();
var httpClient = clientFactory.CreateClient("api");
return Activator.CreateInstance(i, httpClient);
});
});
}
//...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.