Blazor API项目中ILoggerProvider如何获取用户名?
问题描述
我想要构建自定义ILoggerProvider,使其具备获取用户名的功能,该如何实现?我使用外部OIDC服务,在API中已配置如下认证代码:
builder.Services .AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://xxxx/auth/realms/dev"; options.TokenValidationParameters.ValidateAudience = false; });
同时已注册自定义DbLoggerProvider:
builder.Services.AddScoped<ILoggerProvider,DbLoggerProvider>();
当我在DbLoggerProvider的构造函数中注入AuthenticationStateProvider时,构建时出现报错:
System.AggregateException Message=Some services are not able to be constructed Source=Microsoft.Extensions.DependencyInjection StackTrace: at Microsoft.Extensions.DependencyInjection.ServiceProvider. .ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) at Microsoft.Extensions.DependencyInjection.ServiceCollection ContainerBuilderExtensions.BuildServiceProvider (IServiceCollection services, ServiceProviderOptions options) at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build() at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build() at Program.<Main>$(String[] args) in D:\x\Server\Program.cs:line 153 This exception was originally thrown at this call stack: [External Code] Inner Exception 1: InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostApplicationLifetime Lifetime: Singleton ImplementationType: Microsoft.Extensions.Hosting.Internal.ApplicationLifetime': Unable to resolve service for type 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider' while attempting to activate 'x.Server.Core.Providers.DbLoggerProvider'. Inner Exception 2: InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Components.Authorization. AuthenticationStateProvider' while attempting to activate 'x.Server.Core.Providers.DbLoggerProvider'.
在控制器中我通过以下代码获取用户名:
string login = User.Claims.Where(p => p.Type == "preferred_username").First().Value;
请问如何在LoggerProvider中获取该用户名?另外,控制器并非通过AuthenticationStateProvider获取用户对象,它是如何实现的?
解决方案
一、在自定义ILoggerProvider中获取用户名的正确方式
你遇到的注入报错是因为AuthenticationStateProvider是Blazor专用服务,纯ASP.NET Core API场景下不会默认注册;且ILoggerProvider的生命周期是Singleton(即便你注册成Scoped,框架内部也会按Singleton处理),而用户上下文是请求级的(Scoped),直接在Provider构造函数注入请求级服务会导致生命周期冲突。
正确的做法是在自定义Logger(而非LoggerProvider)中通过IHttpContextAccessor获取当前请求的用户上下文,步骤如下:
- 注册
IHttpContextAccessor服务:
builder.Services.AddHttpContextAccessor();
- 修改自定义
DbLoggerProvider,在创建Logger实例时传入IHttpContextAccessor:
public class DbLoggerProvider : ILoggerProvider { private readonly IHttpContextAccessor _httpContextAccessor; public DbLoggerProvider(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public ILogger CreateLogger(string categoryName) { return new DbLogger(_httpContextAccessor); } public void Dispose() { } }
- 在自定义
DbLogger中获取用户名:
public class DbLogger : ILogger { private readonly IHttpContextAccessor _httpContextAccessor; public DbLogger(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public IDisposable BeginScope<TState>(TState state) => null; public bool IsEnabled(LogLevel logLevel) => true; public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { // 获取当前请求的用户名,无用户时返回"匿名用户" var userName = _httpContextAccessor.HttpContext?.User?.Claims .FirstOrDefault(c => c.Type == "preferred_username")?.Value ?? "匿名用户"; // 后续执行写入数据库的逻辑,带上userName即可 // ... } }
- 按框架规范将LoggerProvider注册为Singleton:
builder.Services.AddSingleton<ILoggerProvider, DbLoggerProvider>();
二、控制器中获取用户对象的原理
控制器中的User属性是ASP.NET Core框架自动注入的,本质是直接读取当前请求的HttpContext.User:
- 配置JWT认证后,认证中间件会在请求到达时解析Bearer令牌,将令牌中的声明信息解析到
HttpContext.User中。 - 控制器基类(
ControllerBase)内置了HttpContext属性,可直接访问User;而AuthenticationStateProvider是Blazor场景下专门处理组件用户状态的服务,和API控制器的用户获取逻辑完全无关。
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

