asp.net core webapi 并发请求时 怎么保证实时获取的用户信息是此次请求的?

对于并发请求,每个请求会被分配到一个独立的线程或线程池工作线程上。通过 HttpContextAsyncLocal,每个线程都能独立地获取到它自己的上下文数据。由于这些数据是与当前请求相关的,因此在并发请求时不会互相干扰。

在并发请求时,确保每个请求能够实时获取与之相关的用户信息,主要依赖于以下方法:

  • HttpContext.UserASP.NET Core 内置的请求上下文,保证每个请求独立获取用户信息。

  • AsyncLocal:用于在异步环境中传递请求相关的信息,确保跨线程和异步调用时的正确性。

  • AuthorizationFilterContext .HttpContext.Items:通过拦截器缓存用户信息,可以确保所有后续的请求处理都能正确访问用户信息。

  • 分布式缓存或会话存储:用于分布式应用场景,确保不同服务器上的请求能获取到正确的用户信息。

1. 使用 HTTP 请求上下文(如 HttpContext

ASP.NET Core 中,每个请求都是独立的,并且与当前线程绑定。当你发起一个请求时,ASP.NET Core 会将所有的请求上下文信息(包括认证、用户信息等)存储在 HttpContext 中。这意味着即使你有多个并发请求,每个请求的上下文都是独立的,可以保证每个请求都能获取到与该请求相关的用户信息。

获取用户信息:

你可以通过 HttpContext.User 来获取当前请求的用户信息:

cs 复制代码
public class MyController : ControllerBase
{
    public IActionResult Get()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var username = User.Identity.Name;
        return Ok(new { userId, username });
    }
}

User 属性会在每个请求的上下文中自动填充,在每个请求线程中是独立的,因此不会发生并发请求时的混淆。

2. 使用 AsyncLocal 存储线程相关信息

在某些情况下,你可能需要跨多个方法、类或任务传递请求特定的信息(如用户信息)。AsyncLocal 提供了一种线程和任务绑定的方式,可以确保每个异步操作或线程都能获取到与当前请求相关的信息。

cs 复制代码
public class UserContext
{
    private static AsyncLocal<User> _currentUser = new AsyncLocal<User>();

    public static User CurrentUser
    {
        get => _currentUser.Value;
        set => _currentUser.Value = value;
    }
}

在每个请求的处理过程中,你可以将当前用户信息存储到 AsyncLocal 中,这样即使是异步操作,也能保证用户信息与当前请求相关。

使用方法:

在请求处理中,你可以在认证时设置用户信息:

cs 复制代码
public class MyController : ControllerBase
{
    public IActionResult Get()
    {
        // 假设从某处获取当前用户信息
        var user = new User { UserId = "123", Username = "JohnDoe" };
        UserContext.CurrentUser = user;

        return Ok(UserContext.CurrentUser);
    }
}

这种方法特别适用于需要跨多个异步方法传递用户信息的场景。

3. 使用拦截器IAuthorizationFilter注入用户信息

通过IAuthorizationFilter设置用户信息。这确保每个请求的用户信息都能被正确注入,尤其是在复杂的认证流程中。

cs 复制代码
 public class AuthorizationFilter : IAuthorizationFilter
 {
     public void OnAuthorization(AuthorizationFilterContext context)
     {
         //var user = context.HttpContext.User;
         //if (user == null || !user.HasClaim("role", "Admin"))
         //{
         //    context.Result = new UnauthorizedResult();
         //}
         context.HttpContext.Items["AccountDetail"] = "Admin";
     }
 }

在请求处理中,你可以通过 HttpContext.Items 来获取用户信息:

cs 复制代码
public class MyController : ControllerBase
{
    public IActionResult Get()
    {
        var serviceProvider = HttpContext.RequestServices;
 var account = serviceProvider?.GetRequiredService<IHttpContextAccessor>().HttpContext?.Items["AccountDetail"];
        return Ok(account);
    }
}

注:需提前注入IHttpContextAccessor

cs 复制代码
      // 注册 IHttpContextAccessor
      builder.Services.AddHttpContextAccessor();

4. 使用分布式缓存或会话存储

在分布式应用中,可能无法依赖单一的线程或请求上下文来存储用户信息,这时可以使用分布式缓存(如 Redis)或会话存储来保存每个请求的用户信息。这确保了无论请求在哪个服务器或进程中被处理,用户信息始终能够正确获取。

使用会话存储:

ASP.NET Core 提供了内置的会话机制,可以在请求之间存储用户信息。

cs 复制代码
public class MyController : ControllerBase
{
    public IActionResult Get()
    {
        // 假设你已经将用户信息存入 Session 中
        var userId = HttpContext.Session.GetString("UserId");
        return Ok(new { userId });
    }
}

在中间件或认证过程中,可以设置用户信息到会话:

cs 复制代码
public class UserContextMiddleware
{
    private readonly RequestDelegate _next;

    public UserContextMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var user = new User { UserId = "123", Username = "JohnDoe" };
        context.Session.SetString("UserId", user.UserId);

        await _next(context);
    }
}
相关推荐
渣哥2 分钟前
从代理到切面:Spring AOP 的本质与应用场景解析
javascript·后端·面试
文心快码BaiduComate19 分钟前
文心快码3.5S实测插件开发,Architect模式令人惊艳
前端·后端·架构
5pace24 分钟前
【JavaWeb|第二篇】SpringBoot篇
java·spring boot·后端
HenryLin25 分钟前
Kronos核心概念解析
后端
oak隔壁找我25 分钟前
Spring AOP源码深度解析
java·后端
货拉拉技术28 分钟前
大规模 Kafka 消费集群调度方案
后端
oak隔壁找我28 分钟前
MyBatis Plus 源码深度解析
java·后端
oak隔壁找我28 分钟前
Druid 数据库连接池源码详细解析
java·数据库·后端
剽悍一小兔28 分钟前
Nginx 基本使用配置大全
后端
LCG元29 分钟前
性能排查必看!当Linux服务器CPU/内存飙高,如何快速定位并"干掉"罪魁祸首进程?
linux·后端