ASP.NET MVC:Wie finde ich Controller mit [Authorize]-Attributen mit Reflection in C#? (oder Wie erstelle ich dynamische Site.Master-Menüs?)

ASP.NET MVC:Wie finde ich Controller mit [Authorize]-Attributen mit Reflection in C#? (oder Wie erstelle ich dynamische Site.Master-Menüs?)

Ich bevorzuge es, auf alles in meinen Menüs zu verlinken und einen HtmlHelper zu erstellen, der prüft, ob ein Link zugänglich ist oder nicht, basierend auf den [Authorize]-Attributen.


Ok, also habe ich beschlossen, meine eigene Extended Controller-Klasse zu konkretisieren, wie ich es ursprünglich vorgeschlagen hatte. Hier ist eine sehr einfache Version. Ich sehe verschiedene Möglichkeiten, dies zu verbessern (weiter zu erweitern, den Code zu straffen usw.), aber ich dachte, ich würde meine grundlegenden Ergebnisse anbieten, weil ich mir vorstelle, dass es viele andere Leute gibt, die etwas Ähnliches wollen, aber vielleicht nicht alle wollen die Extras.

public abstract class ExtController : Controller
{
    protected static Dictionary<string, List<string>> RolesControllerDictionary;
    protected override void OnActionExecuted(ActionExecutedContext filterContext)   
    {   
        // build list of menu items based on user's permissions, and add it to ViewData  
        IEnumerable<MenuItem> menu = BuildMenu();  
        ViewData["Menu"] = menu;
    }

    private IEnumerable<MenuItem> BuildMenu()
    {
        // Code to build a menu
        var dynamicMenu = new List<MenuItem>();
        SomeRoleProvider rp = new SomeRoleProvider();
        // ^^^^^INSERT DESIRED ROLE PROVIDER HERE^^^^^
        rp.Initialize("", new NameValueCollection());
        try
        {   // Get all roles for user from RoleProvider
            foreach (var role in rp.GetRolesForUser(HttpContext.User.Identity.Name))
            {   // Check if role is in dictionary
                if (RolesControllerDictionary.Keys.Contains(role))
                {   
                    var controllerList = RolesControllerDictionary[role];
                    foreach (var controller in controllerList)
                    {   // Add controller to menu only if it is not already added
                        if (dynamicMenu.Any(x => x.Text == controller))
                        { continue; }
                        else
                        { dynamicMenu.Add(new MenuItem(controller)); }
                    }
                }
            }
        }
        catch { }   // Most role providers can throw exceptions. Insert Log4NET or equiv here.   
        return dynamicMenu; 
    }

    public ExtController()
    {
        // Check if ControllerRolesDictionary is non-existant
        if (RolesControllerDictionary == null)
        {
            RolesControllerDictionary = new Dictionary<string, List<string>>();
            // If so, use Reflection to add List of all Roles associated with Controllers
            const bool allInherited = true;
            const string CONTROLLER = "Controller";
            var myAssembly = System.Reflection.Assembly.GetExecutingAssembly();

            // get List of all Controllers with [Authorize] attribute
            var controllerList = from type in myAssembly.GetTypes()
                                 where type.Name.Contains(CONTROLLER)
                                 where !type.IsAbstract
                                 let attribs = type.GetCustomAttributes(allInherited)
                                 where attribs.Any(x => x.GetType().Equals(typeof(AuthorizeAttribute)))
                                 select type;
            // Loop over all controllers
            foreach (var controller in controllerList)
            {   // Find first instance of [Authorize] attribute
                var attrib = controller.GetCustomAttributes(allInherited).First(x => x.GetType().Equals(typeof(AuthorizeAttribute))) as AuthorizeAttribute;
                foreach (var role in attrib.Roles.Split(',').AsEnumerable())
                {   // If there are Roles associated with [Authorize] iterate over them
                    if (!RolesControllerDictionary.ContainsKey(role))
                    { RolesControllerDictionary[role] = new List<string>(); }
                    // Add controller to List of controllers associated with role (removing "controller" from name)
                    RolesControllerDictionary[role].Add(controller.Name.Replace(CONTROLLER,""));
                }
            }
        }
    }
}

Zur Verwendung einfach:

  • Fügen Sie [Authorize(Roles="SomeRole1,SomeRole2,SomeRole3,etc."]] zur Controller-Klasse hinzu
  • Ersetzen Sie den geerbten "Controller" durch "ExtController".

Zum Beispiel:

[Authorize(Roles = "Biologist,Admin")]
public class BiologistController : ExtController
{
    public ActionResult Index()
    { return View(); }
}

Wenn Sie „Controller“ nicht durch „ExtController“ ersetzen, hat dieser Controller kein dynamisches Menü. (Das könnte in manchen Szenarien nützlich sein, denke ich...)

In meinem Site.Master Datei habe ich den Abschnitt Menü" geändert so aussehen:

<ul id="menu">              
    <li><%= Html.ActionLink("Home", "Index", "Home")%></li>
    <%  if (ViewData.Keys.Contains("Menu"))
        {
          foreach (MenuItem menu in (IEnumerable<MenuItem>)ViewData["Menu"])
          { %>
    <li><%= Html.ActionLink(menu.Text, "Index", menu.Text)%></li>           
     <%   } 
        }   
     %>       
    <li><%= Html.ActionLink("About", "About", "Home")%></li>
</ul>

Und das ist es! :-)