如何在控制器外的后台服务中获取当前登录用户ID,解决空引用报错
问题根本原因
IHttpContextAccessor 只能获取到当前正在处理的HTTP请求对应的上下文,而Background Service的执行逻辑完全独立于HTTP请求管道,你代码里的DoWork是定时10秒执行一次,执行时没有任何关联的HTTP请求,所以_contextAccessor.HttpContext为null,访问它的User属性自然触发空引用报错。
解决方案
根据你的业务需求,可以选择以下适配方案:
方案1:后台任务由用户的HTTP请求触发
如果这个任务是用户在前端操作后触发的,不要直接在后台服务里拿上下文,而是在触发请求的控制器里先拿到用户ID,再把用户ID作为参数传给后台任务执行逻辑。
示例修改:
- 修改
ScopedProcessingService,新增带用户ID参数的执行方法:
public async Task DoWorkForUser(string userId, CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); if (!string.IsNullOrEmpty(userId)) { var result = await eweUserManager.FindAsync(userId); if (result.IsSuccess) { // 剩余业务逻辑 } } } catch(Exception ex) { logger.LogError(ex, ex.Message); } } }
- 在触发任务的控制器中获取用户ID后调用对应方法:
public class SomeController : ControllerBase { private readonly IScopedProcessingService _processingService; public SomeController(IScopedProcessingService processingService) { _processingService = processingService; } public async Task<IActionResult> TriggerTask() { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // 启动后台任务,长时任务建议配合IHostApplicationLifetime使用取消令牌 _ = _processingService.DoWorkForUser(userId, HttpContext.RequestAborted); return Ok(); } }
方案2:全局定时任务,需要针对所有登录用户执行逻辑
这种场景下不需要绑定单个请求的用户,直接从用户存储中查询所有需要处理的有效用户,遍历执行逻辑即可:
public async Task DoWork(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); // 直接查询所有需要处理的用户 var usersResult = await eweUserManager.GetAllValidUsersAsync(); if (usersResult.IsSuccess) { foreach (var user in usersResult.Data) { // 对每个用户执行业务逻辑 } } } catch(Exception ex) { logger.LogError(ex, ex.Message); } } }
方案3:特殊场景必须保留原有访问HttpContext的逻辑(极不推荐,仅做兼容参考)
如果确实需要在后台服务中临时拿到当前请求的上下文,首先要在Program.cs中注册IHttpContextAccessor服务:
builder.Services.AddHttpContextAccessor();
注意该逻辑只能在有HTTP请求触发的代码块中调用,独立定时循环里没有请求的场景下HttpContext必然为null,调用前必须加空判断:
var userId = _contextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
内容的提问来源于stack exchange,提问作者Rushdi Eskandar
相关产品推荐
相关产品推荐

