Blazor .NET 8中如何从Layout向页面传递对象?
可行的解决方案
1. 利用Cookie传递数据
Cookie是SSR和客户端Blazor都能访问的存储方式,且支持在OnInitializedAsync中操作:
- SSR布局端:注入
IHttpContextAccessor,在OnInitializedAsync中将数据写入Cookie:@inject IHttpContextAccessor HttpContextAccessor @code { protected override async Task OnInitializedAsync() { var userData = new { UserId = "123", Theme = "dark" }; var jsonData = System.Text.Json.JsonSerializer.Serialize(userData); HttpContextAccessor.HttpContext.Response.Cookies.Append( "AppUserData", jsonData, new CookieOptions { HttpOnly = false, SameSite = SameSiteMode.Strict } ); // 同时设置当前作用域服务的属性 YourScopedService.SetUserData(userData); } } - 客户端页面端:注入
IJSRuntime,在OnInitializedAsync中读取Cookie并同步到作用域服务:
注意:设置@inject IJSRuntime JSRuntime @inject YourScopedService ScopedService @code { protected override async Task OnInitializedAsync() { var cookieValue = await JSRuntime.InvokeAsync<string>("eval", "document.cookie.split('; ').find(row => row.startsWith('AppUserData='))?.split('=')[1] ?? ''"); if (!string.IsNullOrEmpty(cookieValue)) { var userData = System.Text.Json.JsonSerializer.Deserialize<UserDataModel>(cookieValue); ScopedService.SetUserData(userData); } } }HttpOnly = false才能让客户端JS读取Cookie,若数据敏感,建议加密后存储。
2. 嵌入全局脚本变量
在SSR布局中将数据序列化后嵌入页面的全局JS变量,客户端直接读取:
- SSR布局端:在页面底部添加脚本块,将数据序列化后赋值给全局变量:
@inject YourScopedService ScopedService <!-- 布局内容 --> <script> window.appUserData = @System.Text.Json.JsonSerializer.Serialize(ScopedService.UserData); </script> - 客户端页面端:在
OnInitializedAsync中通过IJSRuntime读取全局变量:
此方法无需额外API调用,数据随页面初始加载直接传递,性能较好。@inject IJSRuntime JSRuntime @inject YourScopedService ScopedService @code { protected override async Task OnInitializedAsync() { var userData = await JSRuntime.InvokeAsync<UserDataModel>("eval", "window.appUserData"); if (userData != null) { ScopedService.SetUserData(userData); } } }
3. 基于Session的API获取
若数据敏感,可将数据存储在服务器Session中,客户端通过API获取:
- 配置Session:在
Program.cs中添加Session支持:builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.SameSite = SameSiteMode.Strict; }); app.UseSession(); - SSR布局端:将数据存入Session:
@inject IHttpContextAccessor HttpContextAccessor @inject YourScopedService ScopedService @code { protected override async Task OnInitializedAsync() { var userData = ScopedService.UserData; var jsonData = System.Text.Json.JsonSerializer.Serialize(userData); HttpContextAccessor.HttpContext.Session.SetString("AppUserData", jsonData); } } - 添加API端点:在服务器项目中创建最小API,用于获取Session数据:
app.MapGet("/api/userdata", (HttpContext context) => { var jsonData = context.Session.GetString("AppUserData"); if (string.IsNullOrEmpty(jsonData)) return Results.NotFound(); var userData = System.Text.Json.JsonSerializer.Deserialize<UserDataModel>(jsonData); return Results.Ok(userData); }); - 客户端页面端:在
OnInitializedAsync中调用API获取数据:
此方法适合敏感数据,因为数据不会暴露在客户端,仅通过API传递。@inject HttpClient HttpClient @inject YourScopedService ScopedService @code { protected override async Task OnInitializedAsync() { try { var userData = await HttpClient.GetFromJsonAsync<UserDataModel>("/api/userdata"); if (userData != null) { ScopedService.SetUserData(userData); } } catch (HttpRequestException) { // 处理异常 } } }
4. 封装Cookie的自定义作用域服务
创建一个封装Cookie读写的作用域服务,让SSR和客户端共享同一套数据访问逻辑:
public class UserDataService { private readonly IHttpContextAccessor? _httpContextAccessor; private readonly IJSRuntime? _jsRuntime; private UserDataModel? _cachedData; public UserDataService(IHttpContextAccessor? httpContextAccessor, IJSRuntime? jsRuntime) { _httpContextAccessor = httpContextAccessor; _jsRuntime = jsRuntime; } public async Task<UserDataModel?> GetUserDataAsync() { if (_cachedData != null) return _cachedData; if (_httpContextAccessor != null && _httpContextAccessor.HttpContext != null) { // SSR环境:从Cookie读取 var cookieValue = _httpContextAccessor.HttpContext.Request.Cookies["AppUserData"]; if (!string.IsNullOrEmpty(cookieValue)) { _cachedData = System.Text.Json.JsonSerializer.Deserialize<UserDataModel>(cookieValue); } } else if (_jsRuntime != null) { // 客户端环境:从Cookie读取 var cookieValue = await _jsRuntime.InvokeAsync<string>("eval", "document.cookie.split('; ').find(row => row.startsWith('AppUserData='))?.split('=')[1] ?? ''"); if (!string.IsNullOrEmpty(cookieValue)) { _cachedData = System.Text.Json.JsonSerializer.Deserialize<UserDataModel>(cookieValue); } } return _cachedData; } public async Task SetUserDataAsync(UserDataModel data) { _cachedData = data; var jsonData = System.Text.Json.JsonSerializer.Serialize(data); if (_httpContextAccessor != null && _httpContextAccessor.HttpContext != null) { // SSR环境:写入Cookie _httpContextAccessor.HttpContext.Response.Cookies.Append( "AppUserData", jsonData, new CookieOptions { HttpOnly = false, SameSite = SameSiteMode.Strict, Expires = DateTimeOffset.Now.AddDays(1) } ); } else if (_jsRuntime != null) { // 客户端环境:写入Cookie await _jsRuntime.InvokeVoidAsync("eval", $"document.cookie = 'AppUserData={jsonData}; path=/; SameSite=Strict; expires={DateTimeOffset.Now.AddDays(1).ToString("R")}'"); } } }
然后在Program.cs中注册服务:
builder.Services.AddScoped<UserDataService>(); builder.Services.AddHttpContextAccessor();
这样不管是SSR布局还是客户端页面,直接注入UserDataService调用Get/Set方法即可,无需关心当前渲染模式,数据自动通过Cookie同步。
内容的提问来源于stack exchange,提问作者michaeld
相关产品推荐
相关产品推荐

