zoukankan      html  css  js  c++  java
  • MVC 6动态导航菜单从数据库

    表的内容 目标介绍组件使用创建项目迁移数据服务导航菜单下一个结论历史 目标 几年前,我不得不从数据库加载导航菜单并使用web forms创建菜单控件,所以从数据库加载菜单数据的主要思想是根据用户角色进行过滤。最后,我们将按角色过滤数据。这里我们使用ASP。NET Core 2.2 MVC应用程序。 介绍 我在MVC 6。net Core中遇到了这个需求,从数据库中动态生成一个基于角色的导航菜单,这样它就可以用来探索网站和管理面板,用于管理分配角色,权限和其他应用程序的维护。在这个系统中,角色的数量是有限的,因此基于角色的授权是合适的。 组件使用 下面是构建和测试提供的演示代码所需的组件。 下载最新的Visual Studio 2019社区版如果你没有专业版或企业版,我使用的是SQL server Developer edition 17.9.1,你可以从这个链接下载。 创建Web项目 在Visual Studio 2019中创建您的Web应用程序。 选择语言为c#,项目类型为Web,然后选择第一个模板,ASP。NET Core Web应用程序并单击Next。 提供项目名称并选择物理路径,然后单击Create。 选择Web应用程序(模型-视图-控制器),然后单击右边Authentication下面的Change按钮。在此之后,选择单个用户帐户,ok关闭弹出,然后创建。 现在项目已经设置好,可以运行了,但是我们还没有基于我们的模型创建任何数据库,所以首先,我们需要更改appsettings中的连接字符串。json文件。我将使用本地主机作为我的服务器与Windows身份验证,下面是我的连接字符串。 隐藏,复制Code

    "DefaultConnection": "Server=localhost;Database=DynamicMenu;
    Trusted_Connection=True;MultipleActiveResultSets=true"

    但如果我们在这个级别创建数据库,我们将只有标识表,如下所示: 但在本例中,我们还需要两个表,我们将首先使用代码通过定义它们的实体来创建它们,然后将它们添加到上下文类中。 隐藏,收缩,复制Code

    [Table(name: "AspNetRoleMenuPermission")]
    public class RoleMenuPermission
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }
    
        [ForeignKey("ApplicationRole")]
        public string RoleId { get; set; }
    
        [ForeignKey("NavigationMenu")]
        public Guid NavigationMenuId { get; set; }
    
        public NavigationMenu NavigationMenu { get; set; }
    }
    
    [Table(name: "AspNetNavigationMenu")]
    public class NavigationMenu
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }
    
        public string Name { get; set; }
    
        [ForeignKey("ParentNavigationMenu")]
        public Guid? ParentMenuId { get; set; }
    
        public virtual NavigationMenu ParentNavigationMenu { get; set; }
    
        public string ControllerName { get; set; }
    
        public string ActionName { get; set; }
    
        [NotMapped]
        public bool Permitted { get; set; }
    }
    
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
    
        }
    
        public DbSet<RoleMenuPermission> RoleMenuPermission { get; set; }
    
        public DbSet<NavigationMenu> NavigationMenu { get; set; }
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }
    }
    

    迁移 现在我们需要运行迁移,然后更新数据库,Enable-Migrations命令已经过时了,所以我们需要从migrations文件夹中删除所有内容,然后运行add migration命令。 隐藏,复制Code

    add-migration InitialVersion

    它会在Migrations文件夹中创建一些文件,然后我们需要运行update-database命令,如果你的连接字符串是正确的,那么它会像下面这样创建你的数据库: 关于种子数据的更多细节,你可以查看我的另一篇文章: 种子数据MVC 6。net核心应用程序 对于当前场景,我们的种子将拥有所有导航菜单项、用户、角色和权限。所以它会有点复杂。 现在我们已经有了包含所有实体的数据库,所以让我们在开发环境中运行应用程序,它将在数据库中插入种子数据。 数据服务 我们将创建一个数据服务来与数据库通信,它非常简单,它有一个主要函数GetMenuItemsAsync,该函数在按角色过滤后返回导航菜单视图模型。 隐藏,收缩,复制Code

    public class DataAccessService : IDataAccessService
    {
         private readonly ApplicationDbContext _context;
    
         public DataAccessService(ApplicationDbContext context)
         {
             _context = context;
         }
    
         public async Task<List<NavigationMenuViewModel>> 
                           GetMenuItemsAsync(ClaimsPrincipal principal)
         {
             var isAuthenticated = principal.Identity.IsAuthenticated;
             if (!isAuthenticated)
                 return new List<NavigationMenuViewModel>();
    
             var roleIds = await GetUserRoleIds(principal);
             var data = await (from menu in _context.RoleMenuPermission
                               where roleIds.Contains(menu.RoleId)
                               select menu)
                               .Select(m => new NavigationMenuViewModel()
                               {
                                   Id = m.NavigationMenu.Id,
                                   Name = m.NavigationMenu.Name,
                                   ActionName = m.NavigationMenu.ActionName,
                                   ControllerName = m.NavigationMenu.ControllerName,
                                   ParentMenuId = m.NavigationMenu.ParentMenuId,
                               }).Distinct().ToListAsync();
    
             return data;
         }
    
         private async Task<List<string>> GetUserRoleIds(ClaimsPrincipal ctx)
         {
             var userId = GetUserId(ctx);
             var data = await (from role in _context.UserRoles
                               where role.UserId == userId
                               select role.RoleId).ToListAsync();
    
             return data;
         }
    
         private string GetUserId(ClaimsPrincipal user)
         {
             return ((ClaimsIdentity)user.Identity).FindFirst(ClaimTypes.NameIdentifier)?.Value;
         }
    }

    我们需要在Startup.cs中注册这个服务,这样依赖注入就可以为它提供服务。可以这样注册: 隐藏,复制Code

    services.AddScoped<IDataAccessService, DataAccessService>();

    导航菜单 我们将使用视图组件加载导航菜单作为一个部分视图: 隐藏,复制Code

    public class NavigationMenuViewComponent : ViewComponent
    {
        private readonly IDataAccessService _dataAccessService;
    
        public NavigationMenuViewComponent(IDataAccessService dataAccessService)
        {
            _dataAccessService = dataAccessService;
        }
    
        public async Task<IViewComponentResult> InvokeAsync()
        {
            var items = await _dataAccessService.GetMenuItemsAsync(HttpContext.User);
    
            return View(items);
        }
    }

    在Views中的共享文件夹中创建Components文件夹。在组件中,我们可以创建NavigationMenu文件夹,然后是Default。cshtml视图文件。在这里,层级结构对于它的工作非常重要。 这是部分视图HTML,这里我们将保持我们的范围在2级菜单只有它可以递归到N级但这里为了限制它,我们不使用它。 隐藏,收缩,复制Code

    @model List<Mvc.DynamicMenu.Models.NavigationMenuViewModel>
    @{
        ViewData["Title"] = "NavigationMenu";
    }
    <aclass="navbar-brand"asp-area=""asp-controller="Home"asp-action="Index">Dynamic Menu</a>
    <buttonclass="navbar-toggler"type="button"data-toggle="collapse"data-target="#navbarsExampleDefault"aria-controls="navbarsExampleDefault"aria-expanded="false"aria-label="Toggle navigation">
        <spanclass="navbar-toggler-icon"></span>
    </button>
    <divclass="collapse navbar-collapse"id="navbarsExampleDefault">
        <ulclass="navbar-nav mr-auto">
            <liclass="nav-item">
                <aclass="nav-link text"asp-area=""asp-controller="Home"asp-action="Index">Home</a>
            </li>
            <liclass="nav-item">
                <aclass="nav-link text"asp-area=""asp-controller="Home"asp-action="Privacy">Privacy Policy</a>
            </li>
    
            @*Menu Items from the database*@
            
            @foreach (var item in Model)
            {
                if (item.ParentMenuId == null) //Level one items will have null parent id
                {
                    if (!string.IsNullOrWhiteSpace(item.ControllerName))
                    {
                        <liclass="nav-item active">
                            <aclass="nav-link text"asp-area=""asp-controller="@item.ControllerName"asp-action="@item.ActionName">@item.Name</a>
                        </li>
                    }
                    var children = Model.Where(x => x.ParentMenuId == item.Id).ToList();
                    if (children != null) //Level one item has children so append them
                    {
                        <liclass="nav-item dropdown">
                            <aclass="nav-link dropdown-toggle"href="#"id="dropdown01"data-toggle="dropdown"aria-haspopup="true"aria-expanded="false">
                             @item.Name</a>
                            <divclass="dropdown-menu"aria-labelledby="dropdown01">
                                @foreach (var itm in children)
                                {
                                    <aclass="dropdown-item"asp-area=""asp-controller="@itm.ControllerName"asp-action="@itm.ActionName">@itm.Name</a>
                                }
                            </div>
                        </li>
                    }
                }
            }
        </ul>
        <partialname="_LoginPartial"/>
    </div>

    现在我们将创建一个名为Administration的控制器,它有两个动作,角色和用户。 隐藏,复制Code

    public class AdministrationController : Controller
    {
         private readonly UserManager<IdentityUser> _userManager;
         private readonly RoleManager<IdentityRole> _roleManager;
         private readonly ILogger<AdministrationController> _logger;
    
         public AdministrationController(
                 UserManager<IdentityUser> userManager,
                 RoleManager<IdentityRole> roleManager,
                 ILogger<AdministrationController> logger)
         {
             _userManager = userManager;
             _roleManager = roleManager;
             _logger = logger;
         }
    
         public async Task<IActionResult> Roles()
         {
                .......
         }
    
         public async Task<IActionResult> Users()
         {
                ........
         }
    }

    在控制器之后,我们将为这些操作创建视图,在其中我们可以分别显示角色和用户列表。 让我们再次启动应用程序,它看起来是这样的,对于任何访问者,页面看起来是这样的,但是它会根据分配给用户的角色加载额外的菜单项。 让我们使用用户admin@test.com登录。现在,页面看起来如下所示,其中包含允许根据其角色进行管理的附加菜单项。 下面是如何在登录后用部分视图绘制菜单的。 什么下一个 现在我们有一个问题,如果有人知道页面的URL(比如https://localhost/admination/roles),他们仍然可以访问页面。接下来,我们将了解如何进行基于角色的授权。 结论 当我们通过创建数据库时,我们已经实现了从数据库创建导航菜单的目标h迁移并在开发环境下启动了我们的项目。登录用户可以根据其角色查看菜单项。源代码附呈。我鼓励你跑去看看。如有任何问题或建议,欢迎大家发表意见。感谢你的阅读。 历史 2019年8月26日:初版 本文转载于:http://www.diyabc.com/frontweb/news17318.html

  • 相关阅读:
    解析Jquery取得iframe中元素的几种方法
    jquery 金额转换成大写
    MVC 后台管理框架 FineUIMvc 在线示例
    7个高性能JavaScript代码高亮插件
    layer弹出信息框API
    【Bootstrap-插件使用】Jcrop+fileinput组合实现头像上传功能
    一个基于Microsoft Azure、ASP.NET Core和Docker的博客系统
    ASP.NET MVC 3 技术(九) 301永久重定向不带www域名到带www的域名
    ASP.NET MVC 3 网站优化总结(三)Specify Vary: Accept-Encoding header
    ASP.NET MVC 3 网站优化总结(一) 使用 Gzip 压缩
  • 原文地址:https://www.cnblogs.com/Dincat/p/13494066.html
Copyright © 2011-2022 走看看