Dynamic Menus in ASP.NET Core
By FoxLearn 2/28/2025 4:44:49 AM 110
1. Enable Sessions in ASP.NET Core
Sessions are used to store user-specific data, such as menu items, across requests. To enable sessions, configure them in the Program.cs
file.
var builder = WebApplication.CreateBuilder(args); // Add session services builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); // Session timeout options.Cookie.HttpOnly = true; // Prevent client-side script access options.Cookie.IsEssential = true; // Mark the session cookie as essential }); var app = builder.Build(); // Add session middleware to the pipeline app.UseSession(); app.Run();
2. Define the Menu Model
Create a model to represent the menu structure. For example, you can define a Menu
class with properties like Id
, Name
, Action
, and SubMenus
.
public class Menu { public int Id { get; set; } public string Name { get; set; } public string Action { get; set; } public string Controller { get; set; } public List<Menu> SubMenus { get; set; } = new List<Menu>(); }
3. Store Menu Data in Session
After a user logs in or during application startup, fetch the menu data (e.g., from a database or configuration) and store it in the session.
public class AccountController : Controller { [HttpPost] public async Task<IActionResult> Login(LoginModel model) { if (ModelState.IsValid) { // Authenticate the user var user = await _userService.AuthenticateAsync(model.Username, model.Password); if (user != null) { // Fetch menu data based on user role var menus = await _menuService.GetMenusByRoleAsync(user.Role); // Store menu data in session HttpContext.Session.SetString("Menus", JsonConvert.SerializeObject(menus)); return RedirectToAction("Dashboard", "Home"); } else { ModelState.AddModelError("", "Invalid username or password."); } } return View(model); } }
4. Retrieve and Display Menu in _Layout.cshtml
In the layout page (_Layout.cshtml
), retrieve the menu data from the session and render it dynamically.
Inject IHttpContextAccessor:
Inject IHttpContextAccessor
to access the session in the view.
@inject IHttpContextAccessor HttpContextAccessor @using Newtonsoft.Json
Retrieve Menu Data: Deserialize the menu data from the session.
@{ var menusJson = HttpContextAccessor.HttpContext?.Session.GetString("Menus"); List<Menu> menus = new List<Menu>(); if (!string.IsNullOrEmpty(menusJson)) { menus = JsonConvert.DeserializeObject<List<Menu>>(menusJson); } }
Render the Menu: Use a loop to render the menu dynamically.
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" href="/">My App</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> @if (menus != null) { @foreach (var menu in menus) { <li class="nav-item"> <a class="nav-link" href="@Url.Action(menu.Action, menu.Controller)">@menu.Name</a> @if (menu.SubMenus != null && menu.SubMenus.Any()) { <ul class="dropdown-menu"> @foreach (var subMenu in menu.SubMenus) { <li><a class="dropdown-item" href="@Url.Action(subMenu.Action, subMenu.Controller)">@subMenu.Name</a></li> } </ul> } </li> } } </ul> </div> </div> </nav>
Use View Components for Reusability
For better organization, you can encapsulate the menu rendering logic in a View Component.
Create a View Component: Create a MenuViewComponent
class.
public class MenuViewComponent : ViewComponent { private readonly IHttpContextAccessor _httpContextAccessor; public MenuViewComponent(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public async Task<IViewComponentResult> InvokeAsync() { var menusJson = _httpContextAccessor.HttpContext?.Session.GetString("Menus"); List<Menu> menus = new List<Menu>(); if (!string.IsNullOrEmpty(menusJson)) { menus = JsonConvert.DeserializeObject<List<Menu>>(menusJson); } return View(menus); } }
Create the View Component View: Create a view for the component at Views/Shared/Components/Menu/Default.cshtml
.
@model List<Menu> <ul class="navbar-nav"> @foreach (var menu in Model) { <li class="nav-item"> <a class="nav-link" href="@Url.Action(menu.Action, menu.Controller)">@menu.Name</a> @if (menu.SubMenus != null && menu.SubMenus.Any()) { <ul class="dropdown-menu"> @foreach (var subMenu in menu.SubMenus) { <li><a class="dropdown-item" href="@Url.Action(subMenu.Action, subMenu.Controller)">@subMenu.Name</a></li> } </ul> } </li> } </ul>
Render the View Component in _Layout.cshtml
: Invoke the view component in the layout page.
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" href="/">My App</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> @await Component.InvokeAsync("Menu") </div> </div> </nav>
By following these steps, you can implement dynamic menus in ASP.NET Core that adapt to user roles and permissions. Using sessions and view components ensures a clean, maintainable, and scalable solution.
- How to securely reverse-proxy ASP.NET Core
- How to Retrieve Client IP in ASP.NET Core Behind a Reverse Proxy
- Only one parameter per action may be bound from body in ASP.NET Core
- The request matched multiple endpoints in ASP.NET Core
- How to Create a custom model validation attribute in ASP.NET Core
- How to disable ModelStateInvalidFilter in ASP.NET Core
- How to fix LoginPath not working in ASP.NET Core
- Synchronous operations are disallowed