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

Blazor Server:组件与控制器间的数据共享最佳实践探讨

Blazor Server跨作用域共享OAuth授权码的最佳实践

核心问题本质

Blazor组件和MVC控制器分属不同请求作用域:组件基于长连接的SignalR上下文,控制器是短连接的HTTP请求上下文,因此Scoped服务会各自生成实例,无法直接共享数据。以下是几种实用的实现方案:

1. Singleton服务+用户会话标识存储

实现一个Singleton服务,用线程安全的字典按用户唯一标识(SessionId或用户ID)存储授权码:

public class AuthCodeStorage : IAuthCodeStorage
{
    private readonly ConcurrentDictionary<string, string> _authCodes = new();

    public void StoreAuthCode(string userId, string authCode)
    {
        _authCodes.AddOrUpdate(userId, authCode, (key, old) => authCode);
        // 可选:添加后台定时任务清理过期条目,避免内存溢出
    }

    public string GetAuthCode(string userId)
    {
        _authCodes.TryRemove(userId, out var code);
        return code; // 取完即删,防止授权码重复使用
    }
}
  • 控制器注入IAuthCodeStorage,拿到授权码后用当前用户标识存入
  • Blazor组件注入IAuthCodeStorage,通过AuthenticationStateProvider获取用户标识后取码
  • 注意:必须用ConcurrentDictionary保证线程安全,同时要处理过期数据清理

2. 利用HttpContext.Session

借助ASP.NET Core原生Session机制,步骤如下:

  1. 在Program.cs中启用Session:
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(5);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});
// 中间件顺序:放在UseRouting之后,UseBlazorFrameworkComponents之前
app.UseSession();
  1. 控制器中存储授权码:
public class OAuthCallbackController : Controller
{
    public IActionResult Callback(string code)
    {
        HttpContext.Session.SetString("AuthCode", code);
        return RedirectToPage("/Index"); // 跳回Blazor页面
    }
}
  1. Blazor组件中读取并清理Session:
@inject IHttpContextAccessor HttpContextAccessor

@code {
    private string _authCode;

    protected override void OnInitialized()
    {
        _authCode = HttpContextAccessor.HttpContext?.Session.GetString("AuthCode");
        if (!string.IsNullOrEmpty(_authCode))
        {
            HttpContextAccessor.HttpContext?.Session.Remove("AuthCode");
            // 后续处理授权码逻辑
        }
    }
}
  • 优点:无需自定义服务,依赖原生机制
  • 缺点:仅适合单实例部署,多实例集群需搭配分布式Session(如Redis)

3. 分布式缓存(IDistributedCache)

针对多实例集群场景,用IDistributedCache存储授权码:

// 控制器中存储
public async Task<IActionResult> Callback(string code, IDistributedCache cache)
{
    var sessionId = HttpContext.Session.Id;
    await cache.SetStringAsync($"AuthCode_{sessionId}", code, new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
    });
    return RedirectToPage("/Index");
}

// Blazor组件中读取
@inject IDistributedCache Cache
@inject IHttpContextAccessor HttpContextAccessor

@code {
    private string _authCode;

    protected override async Task OnInitializedAsync()
    {
        var sessionId = HttpContextAccessor.HttpContext?.Session.Id;
        if (sessionId != null)
        {
            _authCode = await Cache.GetStringAsync($"AuthCode_{sessionId}");
            if (_authCode != null)
            {
                await Cache.RemoveAsync($"AuthCode_{sessionId}");
                // 后续处理授权码逻辑
            }
        }
    }
}
  • 优点:支持多实例集群,自带过期清理机制
  • 缺点:需提前配置分布式缓存(如Redis、SQL Server)

Blazor Server跨作用域通信通用方案

除了数据存储类方案,跨作用域(控制器→组件、组件→组件)还可采用:

  • 事件总线:实现Singleton事件总线,控制器发布事件,Blazor组件订阅。示例:
public class AuthCodeReceivedEvent
{
    public string AuthCode { get; set; }
}

public class EventBus
{
    public event Action<AuthCodeReceivedEvent> AuthCodeReceived;

    public void Publish(AuthCodeReceivedEvent ev)
    {
        AuthCodeReceived?.Invoke(ev);
    }
}

控制器发布事件后,组件在OnInitialized中订阅,OnDisposed中取消订阅,更新UI时需用InvokeAsync切换到组件上下文。

  • SignalR定向消息:控制器通过SignalR直接给指定用户的Blazor连接发送消息,需提前跟踪用户的SignalR连接ID。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 10:52:15