如何在ASP.NET MVC导航栏动态更新用户名并跨所有Action保持状态?
解决导航栏无需重复更新ViewBag显示用户名的方案
首先要注意:你当前用静态类Profile存储用户名是线程不安全的,多用户并发访问时会互相覆盖用户信息,建议改用Session、Claims或者认证系统自带的用户标识来存储。
下面是几种无需在每个Action中手动更新ViewBag的方案:
方案1:使用ViewComponent(推荐)
ViewComponent专门用于复用视图逻辑,适合导航栏这种全局复用的部分:
- 创建ViewComponent类:
public class UserProfileViewComponent : ViewComponent { private readonly IUserService _userService; private readonly IHttpContextAccessor _httpContextAccessor; public UserProfileViewComponent(IUserService userService, IHttpContextAccessor httpContextAccessor) { _userService = userService; _httpContextAccessor = httpContextAccessor; } public async Task<IViewComponentResult> InvokeAsync() { string accountId = _httpContextAccessor.HttpContext.Session.GetString("AccoountId"); if (string.IsNullOrEmpty(accountId)) { return View("Empty"); // 返回空视图或者默认内容 } var account = await _userService.GetAccount(accountId); var displayName = $"{account.Firstname.Substring(0, 1)}.{account.Lastname}"; return View("Default", displayName); } }
- 创建对应的视图文件:
在Views/Shared/Components/UserProfile/Default.cshtml中:
<p style="margin:0px"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16"> <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z" /> </svg> @Model </p>
- 在布局视图
_layout.cshtml中替换原来的@ViewBag.Name部分:
<div> @await Component.InvokeAsync("UserProfile") </div>
这样每次渲染布局时,ViewComponent会自动获取用户信息并渲染,无需在任何Action中手动处理。
方案2:使用全局ActionFilter
通过ActionFilter在每个Action执行前统一设置ViewBag:
- 创建ActionFilter类:
public class UserProfileActionFilter : IAsyncActionFilter { private readonly IUserService _userService; private readonly IHttpContextAccessor _httpContextAccessor; public UserProfileActionFilter(IUserService userService, IHttpContextAccessor httpContextAccessor) { _userService = userService; _httpContextAccessor = httpContextAccessor; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { string accountId = _httpContextAccessor.HttpContext.Session.GetString("AccoountId"); if (!string.IsNullOrEmpty(accountId)) { var account = await _userService.GetAccount(accountId); context.Controller.ViewBag.Name = $"{account.Firstname.Substring(0, 1)}.{account.Lastname}"; } await next(); // 继续执行Action } }
- 在Program.cs中注册全局过滤器:
builder.Services.AddControllersWithViews(options => { options.Filters.Add<UserProfileActionFilter>(); });
这样所有Action执行前都会自动设置ViewBag.Name,布局中可以继续使用@ViewBag.Name,无需在每个Action中重复代码。
方案3:直接在布局视图中读取Session并获取用户信息
如果不想额外创建组件或过滤器,可以直接在布局中调用服务获取用户信息(需要注入服务到视图):
- 在
_ViewImports.cshtml中添加服务注入:
@inject IUserService UserService @inject IHttpContextAccessor HttpContextAccessor
- 在布局视图中替换
@ViewBag.Name部分:
<div> @{ string accountId = HttpContextAccessor.HttpContext.Session.GetString("AccoountId"); string displayName = string.Empty; if (!string.IsNullOrEmpty(accountId)) { var account = await UserService.GetAccount(accountId); displayName = $"{account.Firstname.Substring(0, 1)}.{account.Lastname}"; } } <p style="margin:0px"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16"> <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z" /> </svg> @displayName </p> </div>
这种方式直接在布局中处理,无需修改任何控制器代码,但要注意异步操作在视图中的处理。
内容的提问来源于stack exchange,提问作者Tipe
相关产品推荐
相关产品推荐

