You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#的请求间保留用户关联的字段值?

跨请求保留用户绑定的Token(替代Static字段的解决方案)

问题原因

你遇到的核心问题是Scoped服务的生命周期限制:AddScoped注册的服务会为每个HTTP请求创建全新实例。调用SetToken接口时,token存在该请求专属的MyService实例的myToken字段中;而后续调用TriggerPipeline是另一个请求,会生成新的MyService实例,之前的token自然无法复用。

下面给出三种和用户绑定的解决方案,无需使用static字段:


方案1:使用Session存储(适合无认证/会话式场景)

利用ASP.NET Core的Session机制,将token存储在用户会话中,每个用户的Session独立,天然实现用户绑定。

步骤1:配置Session

在Program.cs中添加Session服务和中间件:

// 注册Session服务
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30); // 设置会话超时时间
    options.Cookie.HttpOnly = true; // 防止前端JS访问,提升安全性
    options.Cookie.IsEssential = true; // 标记为必要Cookie,符合GDPR要求
});

// 注册HttpContext访问器(用于在服务中获取Session)
builder.Services.AddHttpContextAccessor();

// 注册Scoped服务
builder.Services.AddScoped<IMyService, MyService>();

// ... 其他中间件配置

// 在UseRouting之后、UseAuthorization之前添加Session中间件
app.UseSession();

步骤2:修改MyService

通过IHttpContextAccessor访问Session,替换原私有字段:

public class MyService: IMyService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public MyService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void SetMyToken(string token)
    {
        Console.WriteLine($"This is the parameter token: {token}");
        // 将token存入当前用户的Session
        _httpContextAccessor.HttpContext.Session.SetString("MyUserToken", token);
        Console.WriteLine($"This is myToken: {token}");
    }

    public async Task<string> TriggerPipeline()
    {
        // 从Session中读取当前用户的token
        var myToken = _httpContextAccessor.HttpContext.Session.GetString("MyUserToken");
        Console.WriteLine($"Calling TriggerPipeline function. myToken: {myToken}");
        // 后续业务逻辑
        return string.Empty;
    }
}

方案2:使用用户Claims(适合已认证系统)

如果你的系统已经实现用户认证(比如JWT、Cookie认证),可以将token添加到用户的Claims集合中,Claims会随每个请求的HttpContext.User传递,天然和用户绑定。

修改SetToken接口

在设置token时将其添加到用户的Claims:

[HttpGet("set-token")]
public async Task<IActionResult> SetToken(string token)
{
    var claimsIdentity = User.Identity as ClaimsIdentity;
    // 先移除旧的token(如果存在)
    var existingClaim = claimsIdentity?.FindFirst("MyUserToken");
    if (existingClaim != null)
    {
        claimsIdentity.RemoveClaim(existingClaim);
    }
    // 添加新的token Claim
    claimsIdentity?.AddClaim(new Claim("MyUserToken", token));
    return Ok("Token set successfully");
}

修改MyService

从HttpContext.User的Claims中读取token:

public class MyService: IMyService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public MyService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<string> TriggerPipeline()
    {
        // 从当前用户的Claims中获取token
        var myToken = _httpContextAccessor.HttpContext.User
            .Claims
            .FirstOrDefault(c => c.Type == "MyUserToken")?
            .Value;
        Console.WriteLine($"Calling TriggerPipeline function. myToken: {myToken}");
        // 后续业务逻辑
        return string.Empty;
    }
}

注意:如果使用JWT认证,需要重新生成JWT并返回给客户端,否则客户端的旧token不会包含新的Claim;如果是Cookie认证,ASP.NET Core会自动更新Cookie中的Claims。


方案3:缓存+用户唯一标识(适合灵活过期/分布式场景)

使用内存缓存或分布式缓存,以用户的唯一标识(如用户ID、SessionId)作为key存储token,既实现用户隔离,又能灵活控制过期时间。

步骤1:注册缓存服务

在Program.cs中添加缓存和HttpContext访问器:

// 注册内存缓存(分布式部署可替换为IDistributedCache)
builder.Services.AddMemoryCache();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IMyService, MyService>();

步骤2:修改MyService

通过缓存存取token,用用户标识作为key:

public class MyService: IMyService
{
    private readonly IMemoryCache _cache;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public MyService(IMemoryCache cache, IHttpContextAccessor httpContextAccessor)
    {
        _cache = cache;
        _httpContextAccessor = httpContextAccessor;
    }

    public void SetMyToken(string token)
    {
        Console.WriteLine($"This is the parameter token: {token}");
        var cacheKey = GetUserCacheKey();
        // 将token存入缓存,设置30分钟过期
        _cache.Set(cacheKey, token, TimeSpan.FromMinutes(30));
        Console.WriteLine($"This is myToken: {token}");
    }

    public async Task<string> TriggerPipeline()
    {
        var cacheKey = GetUserCacheKey();
        // 从缓存中读取当前用户的token
        var myToken = _cache.Get<string>(cacheKey);
        Console.WriteLine($"Calling TriggerPipeline function. myToken: {myToken}");
        // 后续业务逻辑
        return string.Empty;
    }

    // 获取用户唯一缓存key(优先用认证用户ID,否则用SessionId)
    private string GetUserCacheKey()
    {
        var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (!string.IsNullOrEmpty(userId))
        {
            return $"UserToken_{userId}";
        }
        // 未认证用户用SessionId
        return $"SessionToken_{_httpContextAccessor.HttpContext.Session.Id}";
    }
}

方案选择建议

  • 无认证系统:优先选方案1(Session),实现简单,无需额外依赖。
  • 已认证系统:优先选方案2(Claims),和用户身份系统深度整合,无需额外存储。
  • 需要分布式部署或灵活过期策略:选方案3(缓存),可无缝切换为Redis等分布式缓存。

内容的提问来源于stack exchange,提问作者HeyLameRobin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 15:36:18