您当前的位置: 首页 >  数据库

寒冰屋

暂无认证

  • 1浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

来自数据库的MVC 6动态导航菜单

寒冰屋 发布时间:2019-10-15 16:12:43 ,浏览量:1

目录

目标

介绍

使用的组件

创建Web项目

迁移

数据服务

导航菜单

接下来是什么

结论

Download source code - 977.3 KB 

目标

几年前,我不得不从数据库加载导航菜单并使用Web表单创建菜单控件,因此从数据库加载菜单数据的主要思想是根据用户角色进行过滤。最后,我们将按角色过滤数据。在这里,我们必须使用ASP.NET Core 2.2 MVC应用程序来做到这一点。

介绍

我在MVC 6 .NET Core中遇到了此要求,以便从数据库动态生成基于角色的导航菜单,因此可以使用它浏览网站和管理面板以进行管理,以分配角色,权限和其他应用程序维护。在此系统中,角色数量受到限制,因此基于角色的授权非常合适。

使用的组件

这是构建和测试所提供的演示代码所需的组件。

  • 如果您没有Professional或Enterprise,则下载最新的Visual Studio 2019社区版本
  • 我正在使用SQL Server Developer版本17.9.1,可以从此链接下载。
创建Web项目

在Visual Studio 2019中创建Web应用程序。

选择语言作为C#,将项目类型选择为Web,然后选择第一个模板ASP.NET Core Web Application,然后单击下一步。

提供项目名称并选择物理路径,然后单击“创建”。

选择Web应用程序(模型-视图-控制器),然后单击“身份验证”下右侧的“更改”按钮。之后,选择个人用户帐户,单击确定以关闭弹出窗口,然后单击创建。

现在,该项目已经设置并可以运行,但是我们没有基于模型创建任何数据库,因此首先,我们需要在appsettings.json文件中更改连接字符串。我将使用本地主机作为Windows身份验证的服务器,以下是我的连接字符串。

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

但是,如果我们在此级别上创建数据库,则只有Identity如下所示的表:

但是在我们的例子中,我们需要另外两个表,这些表将通过使用代码来创建,首先定义它们的实体,然后将它们添加到我们的context类中。

[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 options)
        : base(options)
    {

    }

    public DbSet RoleMenuPermission { get; set; }

    public DbSet NavigationMenu { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}
迁移

现在我们需要运行迁移,然后更新数据库,Enable-Migrations命令已过时,因此我们需要从Migrations文件夹中删除所有内容,然后运行add migration命令。

add-migration InitialVersion

它将在Migrations文件夹中创建一些文件,然后如果您的连接字符串正确,我们需要运行update-database命令,然后将如下所示创建数据库:

如果想了解更详细的信息,可阅读另一篇文章:

  • 播种数据MVC 6 .NET Core应用程序

对于当前方案,我们的seed将具有所有导航菜单项、用户、角色和权限。因此,它将更加复杂。

现在,我们已经拥有所有实体的数据库,因此让我们在开发环境中运行应用程序,它将在数据库中插入seed数据。

数据服务

我们将创建一个数据服务来与数据库通信,它非常简单,它具有一个主要功能GetMenuItemsAsync ,该功能在按角色过滤后返回Navigation菜单视图模型。

public class DataAccessService : IDataAccessService
{
     private readonly ApplicationDbContext _context;

     public DataAccessService(ApplicationDbContext context)
     {
         _context = context;
     }

     public async Task 
                       GetMenuItemsAsync(ClaimsPrincipal principal)
     {
         var isAuthenticated = principal.Identity.IsAuthenticated;
         if (!isAuthenticated)
             return new List();

         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 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中注册此服务,以便依赖注入可以为其提供服务。可以这样注册:

services.AddScoped();
导航菜单

我们将使用“视图组件”将导航菜单加载为部分视图:

public class NavigationMenuViewComponent : ViewComponent
{
    private readonly IDataAccessService _dataAccessService;

    public NavigationMenuViewComponent(IDataAccessService dataAccessService)
    {
        _dataAccessService = dataAccessService;
    }

    public async Task InvokeAsync()
    {
        var items = await _dataAccessService.GetMenuItemsAsync(HttpContext.User);

        return View(items);
    }
}

在Views的Shared文件夹中创建一个Components文件夹。因此,在Components中,我们可以创建NavigationMenu文件夹,然后创建Default.cshtml视图文件。在这里,该层次结构对于它的工作非常重要。

这是HTML的局部视图,在这里我们将范围保持在2级菜单,只有它可以递归地转到N级,但是为了保持它的有限性,我们将不使用它。

@model List
@{
    ViewData["Title"] = "NavigationMenu";
}
Dynamic Menu

    

  • Home
  • Privacy Policy
  • @*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)) {
  • @item.Name
  • } var children = Model.Where(x => x.ParentMenuId == item.Id).ToList(); if (children != null) //Level one item has children so append them {
  • @item.Name
    @foreach (var itm in children) { @itm.Name }
  • } } }

现在,我们将创建一个名为Administration的控制器,其中包含两个操作Roles 和Users。

public class AdministrationController : Controller
{
     private readonly UserManager _userManager;
     private readonly RoleManager _roleManager;
     private readonly ILogger _logger;

     public AdministrationController(
             UserManager userManager,
             RoleManager roleManager,
             ILogger logger)
     {
         _userManager = userManager;
         _roleManager = roleManager;
         _logger = logger;
     }

     public async Task Roles()
     {
            .......
     }

     public async Task Users()
     {
            ........
     }
}

控制器后,我们将创建视图的这些动作,我们可以分别显示Roles和Users列表。

让我们再次启动该应用程序,以下是它的外观,对于任何访问者来说页面都是这样,但是它将根据分配给用户的角色加载其他菜单项。

让我们以用户admin@test.com登录。现在,该页面如下所示,并根据其角色允许其他菜单项进行管理。

因此,这是登录后如何用部分视图绘制该菜单的方法。

接下来是什么

现在我们遇到一个问题,如果有人知道页面的URL,例如https://localhost/administration/roles,他们仍然可以访问该页面。接下来,我们将看看如何进行基于角色的授权。

结论

通过迁移创建数据库并在开发环境下启动项目时,我们已经实现了从数据库创建导航菜单的目标。登录的用户根据其角色查看菜单项。源代码已随附。我鼓励您运行并查看它。

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0470s