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);
    }
}
相关推荐
呼Lu噜18 分钟前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_15822 分钟前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩35 分钟前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia041235 分钟前
GenericObjectPool——重用你的对象
后端
Piper蛋窝1 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel1 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581361 小时前
什么是MCP
后端·程序员
小鸡脚来咯3 小时前
SpringBoot 常用注解通俗解释
java·spring boot·后端
豌豆花下猫3 小时前
Python 3.14 t-string 要来了,它与 f-string 有何不同?
后端·python·ai