基于用户权限的动态菜单生成
Dynamic Menu generation based on user permissions
我正在尝试开发一个通用功能,可以让我获得应用程序的所有操作和所有控制器。我想通过所有仅 GET 而不是 POST 的操作来过滤这些控制器操作。换句话说,我想在应用程序中拥有所有可能的路由。我正在使用反射来做这件事,但还没有真正能够通过。我想使用反射,所以每次添加任何新的控制器和动作时,它们也会在此列表中。
所以,我想要这个的原因是,我想根据用户的角色、声明、and/or 任何其他用户权限,将这些操作基本上显示为用户的动态菜单。这应该只向他们展示允许的内容。
任何这方面的线索都将不胜感激。
谢谢。
[Table("Menus")]
public class Menu
{
public int ID { get; set; }
public string Name { get; set; }
public string CssClass { get; set; }
public int SortOrder { get; set; }
public MenuLevel MenuLevel { get; set; }
public List<MenuItem> MenuItems { get; set; }
}
[Table("MenuItems")]
public class MenuItem
{
public int ID { get; set; }
public string Name { get; set; }
public string CssClass { get; set; }
public int SortOrder { get; set; }
}
private async Task<List<Menu>> GetAndSaveAllControllerActions()
{
List<Menu> menus = new();
// loop through all potential controller types
foreach (var controllerType in Assembly.GetExecutingAssembly().GetExportedTypes().Where(t => typeof(ControllerBase).IsAssignableFrom(t)))
{
Menu menu = new()
{
Title = controllerType.Name,
MenuLevel = MenuLevel.Level1,
SortOrder = 1,
RouteAddress = @$"\{controllerType.Name}\"
};
menu.Title = menu.Title.Replace("Controller", "");
// get their all public instance methods (excluding inherited ones)
// these are the so-called action methods
foreach (var actionMethod in controllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
// get attributes assigned with the method
var attributes = actionMethod.GetCustomAttributes(false);
// filter methods marked with [NonAction]
// filter out the ones with ref or out params too here if
// you have such things
if (!attributes.Any(i => i is NonActionAttribute))
{
// filter POSTs out as you wish
if (!attributes.Any(i => i is HttpPostAttribute))
{
// you can compose something from various info available at this point e.g.
// through 'controllerType', 'actionMethod' or other attributes if they
// exists in 'attributes' (e.g. RouteAttribute)
menu.MenuItems.Add(new MenuItem()
{
MenuID = menu.MenuID,
Title = actionMethod.Name,
RouteAddress = menu.RouteAddress + $"{actionMethod.Name}"
});
}
}
}
menus.Add(menu);
}
await CheckForDatabaseExistence(menus);
return menus;
}
private Task<int> CheckForDatabaseExistence(List<Menu> menus)
{
var dbMenus = _context.Menus.ToList();
foreach (var menu in menus)
if (!dbMenus.Contains(menu))
{
_context.Menus.Add(menu);
return _context.SaveChangesAsync();
}
return Task.FromResult(0);
}
我已经修改了代码,以便将来对任何人有所帮助。
感谢 cly,他在下面的回答对我们有所帮助。
我相信还有其他方法可以通过 MVC 的路由来完成您提到的这项任务。但是您以 reflection-based 方式开始编写代码,因此最好继续使用这种方式而不是混合使用各种方式。
在 MVC 中,动作方法可以是基于 class 的 Controller
的任何方法,其中:
- 是
public
- 不是
static
- 没有棘手的参数,例如标有
ref
和 out
的参数
- 未标记
[NonAction]
属性
下面的 PoC 代码向您展示了如何使用这些约束来收集您需要的数据。
(代码被实现为 TestMethod
因为测试执行提供了一个非常方便的快速微执行环境,可以在您的开发域的所有内容都可用时临时尝试一些事情。这个假的“测试”可能是如果你想要的东西被尝试过并且你继续前进,就简单地放弃。)
[TestMethod]
public void MyTestMethod()
{
// loop through all potential controller types
foreach (var controllerType in Assembly
.GetExecutingAssembly()
.GetExportedTypes()
.Where(t => typeof(ControllerBase).IsAssignableFrom(t)))
{
// get their all public instance methods (excluding inherited ones)
// these are the so-called action methods
foreach(var actionMethod in controllerType.GetMethods(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.DeclaredOnly))
{
// get attributes assigned with the method
var attributes = actionMethod.GetCustomAttributes(false);
// filter methods marked with [NonAction]
// filter out the ones with ref or out params too here if
// you have such things
if (!attributes.Any(i=>i is System.Web.Http.NonActionAttribute))
{
// filter POSTs out as you wish
if (!attributes.Any(i => i is System.Web.Http.HttpPostAttribute))
{
// you can compose something from various info available at this point e.g.
// through 'controllerType', 'actionMethod' or other attributes if they
// exists in 'attributes' (e.g. RouteAttribute)
var controllerName = controllerType.Name;
var actionName = actionMethod.Name;
}
}
}
}
}
我正在尝试开发一个通用功能,可以让我获得应用程序的所有操作和所有控制器。我想通过所有仅 GET 而不是 POST 的操作来过滤这些控制器操作。换句话说,我想在应用程序中拥有所有可能的路由。我正在使用反射来做这件事,但还没有真正能够通过。我想使用反射,所以每次添加任何新的控制器和动作时,它们也会在此列表中。
所以,我想要这个的原因是,我想根据用户的角色、声明、and/or 任何其他用户权限,将这些操作基本上显示为用户的动态菜单。这应该只向他们展示允许的内容。
任何这方面的线索都将不胜感激。
谢谢。
[Table("Menus")]
public class Menu
{
public int ID { get; set; }
public string Name { get; set; }
public string CssClass { get; set; }
public int SortOrder { get; set; }
public MenuLevel MenuLevel { get; set; }
public List<MenuItem> MenuItems { get; set; }
}
[Table("MenuItems")]
public class MenuItem
{
public int ID { get; set; }
public string Name { get; set; }
public string CssClass { get; set; }
public int SortOrder { get; set; }
}
private async Task<List<Menu>> GetAndSaveAllControllerActions()
{
List<Menu> menus = new();
// loop through all potential controller types
foreach (var controllerType in Assembly.GetExecutingAssembly().GetExportedTypes().Where(t => typeof(ControllerBase).IsAssignableFrom(t)))
{
Menu menu = new()
{
Title = controllerType.Name,
MenuLevel = MenuLevel.Level1,
SortOrder = 1,
RouteAddress = @$"\{controllerType.Name}\"
};
menu.Title = menu.Title.Replace("Controller", "");
// get their all public instance methods (excluding inherited ones)
// these are the so-called action methods
foreach (var actionMethod in controllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
// get attributes assigned with the method
var attributes = actionMethod.GetCustomAttributes(false);
// filter methods marked with [NonAction]
// filter out the ones with ref or out params too here if
// you have such things
if (!attributes.Any(i => i is NonActionAttribute))
{
// filter POSTs out as you wish
if (!attributes.Any(i => i is HttpPostAttribute))
{
// you can compose something from various info available at this point e.g.
// through 'controllerType', 'actionMethod' or other attributes if they
// exists in 'attributes' (e.g. RouteAttribute)
menu.MenuItems.Add(new MenuItem()
{
MenuID = menu.MenuID,
Title = actionMethod.Name,
RouteAddress = menu.RouteAddress + $"{actionMethod.Name}"
});
}
}
}
menus.Add(menu);
}
await CheckForDatabaseExistence(menus);
return menus;
}
private Task<int> CheckForDatabaseExistence(List<Menu> menus)
{
var dbMenus = _context.Menus.ToList();
foreach (var menu in menus)
if (!dbMenus.Contains(menu))
{
_context.Menus.Add(menu);
return _context.SaveChangesAsync();
}
return Task.FromResult(0);
}
我已经修改了代码,以便将来对任何人有所帮助。 感谢 cly,他在下面的回答对我们有所帮助。
我相信还有其他方法可以通过 MVC 的路由来完成您提到的这项任务。但是您以 reflection-based 方式开始编写代码,因此最好继续使用这种方式而不是混合使用各种方式。
在 MVC 中,动作方法可以是基于 class 的 Controller
的任何方法,其中:
- 是
public
- 不是
static
- 没有棘手的参数,例如标有
ref
和out
的参数
- 未标记
[NonAction]
属性
下面的 PoC 代码向您展示了如何使用这些约束来收集您需要的数据。
(代码被实现为 TestMethod
因为测试执行提供了一个非常方便的快速微执行环境,可以在您的开发域的所有内容都可用时临时尝试一些事情。这个假的“测试”可能是如果你想要的东西被尝试过并且你继续前进,就简单地放弃。)
[TestMethod]
public void MyTestMethod()
{
// loop through all potential controller types
foreach (var controllerType in Assembly
.GetExecutingAssembly()
.GetExportedTypes()
.Where(t => typeof(ControllerBase).IsAssignableFrom(t)))
{
// get their all public instance methods (excluding inherited ones)
// these are the so-called action methods
foreach(var actionMethod in controllerType.GetMethods(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.DeclaredOnly))
{
// get attributes assigned with the method
var attributes = actionMethod.GetCustomAttributes(false);
// filter methods marked with [NonAction]
// filter out the ones with ref or out params too here if
// you have such things
if (!attributes.Any(i=>i is System.Web.Http.NonActionAttribute))
{
// filter POSTs out as you wish
if (!attributes.Any(i => i is System.Web.Http.HttpPostAttribute))
{
// you can compose something from various info available at this point e.g.
// through 'controllerType', 'actionMethod' or other attributes if they
// exists in 'attributes' (e.g. RouteAttribute)
var controllerName = controllerType.Name;
var actionName = actionMethod.Name;
}
}
}
}
}