.NET Core中BackgroundService如何传递用户信息至服务层?
问题描述
我在.NET Core Web应用中通过以下方式将用户信息传递到服务层:
services.AddScoped<RequestInfo>(provider => { var context = provider.GetRequiredService<IHttpContextAccessor>(); var userJson = ((ClaimsIdentity)context.HttpContext.User.Identity).FindFirst("LoggedInUser").Value; var user = JsonConvert.DeserializeObject<User>(userJson); return new RequestInfo(int.Parse(user.UserId), user.ClientId); });
这个方案在控制器和服务中能正常工作,但我还有一个处理消息的BackgroundService,希望在解析服务处理消息前,把消息中的用户信息传入。请问是否可以实现?或者有什么替代方案?我预想的伪代码如下:
using var scope = serviceProvider.CreateScope(); SetupUserInfo(..have no access to ServiceCollection .., contract user info); var service = scope.ServiceProvider.GetRequiredService(..my type);
更新:我的初步思路
services.AddScoped<RequestInfo>(); var info = provider.GetRequiredService<RequestInfo>(); info.UserId = .. get info from http context somewhere in middleware (for API) info.UserId = .. get info from message before resolve of main dependency (for Background service)
解决方案
1. 重构RequestInfo设计
先把RequestInfo调整为可动态赋值的结构(如果之前是只读构造注入的话),适配不同场景的赋值需求:
public class RequestInfo { public int UserId { get; set; } public string ClientId { get; set; } }
然后在DI容器中注册为Scoped生命周期:
services.AddScoped<RequestInfo>();
2. API请求场景:中间件赋值
通过自定义中间件从HttpContext提取用户信息,直接注入当前作用域的RequestInfo:
public class RequestInfoMiddleware { private readonly RequestDelegate _next; public RequestInfoMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, RequestInfo requestInfo) { var userJson = ((ClaimsIdentity)context.User.Identity).FindFirst("LoggedInUser")?.Value; if (!string.IsNullOrEmpty(userJson)) { var user = JsonConvert.DeserializeObject<User>(userJson); requestInfo.UserId = int.Parse(user.UserId); requestInfo.ClientId = user.ClientId; } await _next(context); } } // 在Program.cs中注册中间件(注意放在认证中间件之后) app.UseMiddleware<RequestInfoMiddleware>();
3. BackgroundService场景:手动赋值
在后台服务处理消息时,创建作用域后先获取RequestInfo,从消息中提取用户信息赋值,再获取业务服务使用:
public class MessageProcessingService : BackgroundService { private readonly IServiceProvider _serviceProvider; public MessageProcessingService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { // 模拟从消息队列获取消息 var message = await FetchNextMessage(stoppingToken); if (message == null) continue; using var scope = _serviceProvider.CreateScope(); var requestInfo = scope.ServiceProvider.GetRequiredService<RequestInfo>(); // 从消息中提取用户信息并赋值 requestInfo.UserId = int.Parse(message.UserId); requestInfo.ClientId = message.ClientId; // 此时获取业务服务,依赖的RequestInfo已携带用户信息 var businessService = scope.ServiceProvider.GetRequiredService<IMyBusinessService>(); await businessService.ProcessMessage(message, stoppingToken); } } private async Task<Message> FetchNextMessage(CancellationToken stoppingToken) { // 替换为实际的消息队列消费逻辑 await Task.Delay(1000, stoppingToken); return new Message { UserId = "123", ClientId = "Client_001" }; } }
4. 可选优化:确保RequestInfo的完整性
如果需要避免RequestInfo未被正确赋值的情况,可以给它添加初始化校验:
public class RequestInfo { private bool _isInitialized; public int UserId { get; private set; } public string ClientId { get; private set; } public void Initialize(int userId, string clientId) { if (_isInitialized) throw new InvalidOperationException("RequestInfo已完成初始化,不可重复赋值"); UserId = userId; ClientId = clientId; _isInitialized = true; } }
这样在赋值时调用Initialize方法,既能防止重复赋值,也能在业务逻辑中检查_isInitialized状态,避免使用未初始化的用户信息。
内容的提问来源于stack exchange,提问作者Anton Putau
相关产品推荐
相关产品推荐

