.NET Core MVC 6全局共享MS Graph用户信息至导航栏咨询
解决.NET Core MVC 6全局共享用户导航信息的方案
方案一:使用ViewComponent(推荐,组件化设计更易维护)
这种方式把用户信息的获取和渲染封装成独立组件,所有页面都能直接调用,无需每个控制器重复传值。
- 创建ViewComponent类
在项目中新建Components文件夹,添加UserProfileViewComponent.cs:
using Microsoft.AspNetCore.Mvc; using Microsoft.Graph; public class UserProfileViewComponent : ViewComponent { private readonly GraphServiceClient _graphClient; // 注入已注册的GraphServiceClient public UserProfileViewComponent(GraphServiceClient graphClient) { _graphClient = graphClient; } public async Task<IViewComponentResult> InvokeAsync() { // 调用MS Graph获取当前用户核心信息 var user = await _graphClient.Me.Request() .Select(u => new { u.DisplayName, u.MobilePhone, u.Photo }) .GetAsync(); // 封装成ViewModel(可根据需求扩展字段) var viewModel = new UserProfileViewModel { Name = user.DisplayName, Phone = user.MobilePhone, PhotoUrl = user.Photo != null ? Url.Action("GetUserPhoto", "Home") : "/images/default-avatar.png" }; return View(viewModel); } } // 配套的ViewModel public class UserProfileViewModel { public string Name { get; set; } public string Phone { get; set; } public string PhotoUrl { get; set; } }
- 创建ViewComponent视图
在Views/Shared/Components/UserProfile路径下新建Default.cshtml(路径必须严格对应组件名):
<div class="user-nav-info"> <img src="@Model.PhotoUrl" alt="用户头像" class="avatar" /> <div class="user-details"> <span class="user-name">@Model.Name</span> <span class="user-phone">@Model.Phone</span> </div> </div>
- 在导航布局中调用组件
打开_Layout.cshtml(或你的导航所在布局页),替换原_loginPartial的引用为:
@await Component.InvokeAsync("UserProfile")
- Program.cs中确保Graph服务注册
如果还没注册MS Graph服务,在Program.cs中添加:
// 示例:基于Azure AD的Graph服务注册 builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi(new[] { "User.Read" }) .AddMicrosoftGraph(builder.Configuration.GetSection("Graph")) .AddInMemoryTokenCaches();
方案二:使用全局ActionFilter(全局注入ViewData)
通过过滤器在所有Action执行后自动把用户信息注入ViewData,实现全局共享。
- 创建全局ActionFilter类
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Graph; public class UserProfileGlobalFilter : IAsyncActionFilter { private readonly GraphServiceClient _graphClient; public UserProfileGlobalFilter(GraphServiceClient graphClient) { _graphClient = graphClient; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var resultContext = await next(); // 仅对视图结果注入数据 if (resultContext.Result is ViewResult viewResult) { var user = await _graphClient.Me.Request() .Select(u => new { u.DisplayName, u.MobilePhone, u.Photo }) .GetAsync(); viewResult.ViewData["UserName"] = user.DisplayName; viewResult.ViewData["UserPhone"] = user.MobilePhone; viewResult.ViewData["UserPhotoUrl"] = user.Photo != null ? resultContext.HttpContext.Request.PathBase + "/Home/GetUserPhoto" : "/images/default-avatar.png"; } } }
- 在Program.cs注册全局过滤器
builder.Services.AddControllersWithViews(options => { options.Filters.Add<UserProfileGlobalFilter>(); });
- 导航视图中直接使用ViewData
<div class="user-nav-info"> <img src="@ViewData["UserPhotoUrl"]" alt="用户头像" class="avatar" /> <div class="user-details"> <span class="user-name">@ViewData["UserName"]</span> <span class="user-phone">@ViewData["UserPhone"]</span> </div> </div>
补充:用户头像获取接口
如果需要显示用户头像,在Home控制器中添加以下Action:
public async Task<IActionResult> GetUserPhoto() { var graphClient = HttpContext.RequestServices.GetRequiredService<GraphServiceClient>(); try { var photoStream = await graphClient.Me.Photo.Content.Request().GetAsync(); return File(photoStream, "image/jpeg"); } catch { // 头像获取失败时返回默认图 return File("/images/default-avatar.png", "image/png"); } }
优化建议
为避免频繁调用MS Graph,可添加缓存逻辑(比如内存缓存),将用户信息缓存30分钟左右:
// 在ViewComponent或Filter中注入IDistributedCache private readonly IDistributedCache _cache; // 获取缓存或调用Graph var cacheKey = $"UserProfile_{User.Identity.Name}"; var cachedProfile = await _cache.GetAsync<UserProfileViewModel>(cacheKey); if (cachedProfile == null) { // 调用Graph获取数据并缓存 await _cache.SetAsync(cacheKey, viewModel, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) }); } else { viewModel = cachedProfile; }
记得在Program.cs注册缓存服务:
builder.Services.AddDistributedMemoryCache();
内容的提问来源于stack exchange,提问作者Junior.netare
相关产品推荐
相关产品推荐

