2010-12-08 39 views
3

J'ai un certain nombre de contrôleurs dans mon projet qui héritent tous d'un contrôleur que j'ai appelé BaseController. J'ai écrit un attribut personnalisé que j'ai appliqué à toute la classe BaseController, de sorte qu'à chaque fois qu'une action s'exécute dans l'un de mes contrôleurs, cet attribut s'exécute en premier. Le problème est que j'ai quelques actions de contrôleur que je voudrais ignorer cet attribut, mais je ne sais pas comment le faire.ASP.NET MVC: Ignorer un attribut personnalisé dans une classe de contrôleur de base

Quelqu'un peut-il aider? J'utilise MVC 1.

Merci.

+0

pouvez-vous publier votre attribut? cela empêche-t-il l'action d'être frappée? – hunter

+0

Je préfère ne pas publier l'attribut pour des raisons de données propriétaires, mais oui, il peut rediriger l'utilisateur vers la page de connexion plutôt que de permettre à l'action d'être touchée. –

Répondre

9

j'avais un même besoin de quelque chose comme ça et a constaté que, en créant un filtre d'autorisation (mise en œuvre/découlant de FilterAttribute, IAuthorizationFilter) plutôt qu'un filtre d'action ordinaire (provenant de ActionFilterAttribute), et la mise en Inherited=true et AllowMultiple=false sur l'attribut, qu'il ne courrait qu'une seule fois au bon endroit. Cela signifie que je suis capable de "cascader" mon filtre d'un contrôleur de base (la valeur par défaut à l'échelle du site), à ​​un contrôleur dérivé (par exemple AdminController ou autre), ou encore à une méthode d'action individuelle .

Par exemple,

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, Inherited=true, AllowMultiple=false)] 
public class MyCustomAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    private MyCustomMode _Mode; 
    public MyCustomAttribute(MyCustomMode mode) 
    { 
     _Mode = mode; 
    } 
    public virtual void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 
     // run my own logic here. 
     // set the filterContext.Result to anything non-null (such as 
     // a RedirectResult?) to skip the action method's execution. 
     // 
     // 
    } 
} 

public enum MyCustomMode 
{ 
    Enforce, 
    Ignore 
} 

Et puis l'utiliser, je peux l'appliquer à mon super-contrôleur,

[MyCustomAttribute(Ignore)] 
public class BaseController : Controller 
{ 
} 

Et je peux changer/remplacer pour les contrôleurs spécifiques, ou même pour des actions spécifiques!

[MyCustomAttribute(Enforce)] 
public class AdministrationController : BaseController 
{ 
    public ActionResult Index() 
    { 
    } 

    [MyCustomAttribute(Ignore)] 
    public ActionResult SomeBasicPageSuchAsAHelpDocument() 
    { 
    } 
} 

Cela m'a permis de « éteindre » le filtre pour les cas spécifiques, tout en étant capable de l'appliquer par défaut sur le contrôleur soit tout ou ensemble de l'application.

Bonne chance!

+0

Cela semble prometteur. Je vais réviser la façon dont je l'ai fait avant et revenir vers vous avec mes résultats. –

+0

Pour la postérité: Voir aussi http://stackoverflow.com/questions/857142/mvc-attributes-on-controllers-and-actions/15261325#15261325 – Funka

2

Je ne suis pas sûr qu'il existe un moyen facile de supprimer des attributs dans cette situation. Mais j'ai fait quelque chose de similaire pour un projet et ce que j'ai fait, comme c'était seulement dans quelques cas où je ne voulais pas que mon attribut fonctionne, était de créer deux attributs. Mon premier attribut a été appliqué à mon contrôleur de base comme vous l'avez fait mais il était conscient de l'existence d'un second attribut et en implémentant ce second attribut, je pouvais désactiver l'attribut de la classe de base.

Je ne sais pas si c'était la meilleure solution, mais cela a fonctionné pour moi.

Cela a été appliqué au contrôleur de base:

/// <summary> 
/// This is used to force the schema to HTTP is it is HTTPS. 
/// RequireHttpsAttribute or OptionalHttpsAttribute takes precedence if used. 
/// </summary> 
public class RequireHttpAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    public virtual void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
      throw new ArgumentNullException("filterContext"); 

     if (filterContext.HttpContext.Request.IsSecureConnection) 
     { 
      object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(true); 
      if (!attributes.Any(a => a is RequireHttpsAttribute || a is OptionalHttpsAttribute)) 
      { 
       HandleHttpsRequest(filterContext); 
      } 
     } 
    } 

    protected virtual void HandleHttpsRequest(AuthorizationContext filterContext) 
    { 
     // only redirect for GET method, otherwise browser may not propogate the verb and request body correctly 
     if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
      throw new InvalidOperationException(MvcResources.RequireHttpAttribute_MustNotUseSsl); 

     // redirect to HTTP version 
     string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; 
     filterContext.Result = new RedirectResult(url); 
    } 
} 

comme ceci:

[RequireHttp] 
public abstract class Controller : System.Web.Mvc.Controller 
{ 
} 

Je pourrais alors utiliser ce qui est effectivement un attribut factice pour le désactiver.

/// <summary> 
/// This attribute allows the action to be server on HTTP and HTTPS but neither is enforce. 
/// RequireHttpsAttribute takes precedence if used. 
/// </summary> 
public class OptionalHttpsAttribute : FilterAttribute 
{ 
    // This is deliberately empty, the attribute is used by RequireHttpAttribute to stop it changing schema to HTTP 
} 

comme ceci:

[OptionalHttps] 
    public ActionResult OptionalHttps() 
    { 
     return View(); 
    } 
9

Dans votre attribut personnalisé, vous pouvez ajouter cette shouldRun() vérifier comme ceci:

public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (ShouldRun(filterContext)) 
     { 
      // proceed with your code 
     } 
    } 

    private bool ShouldRun(ActionExecutingContext filterContext) 
    { 
     var ignoreAttributes = filterContext.ActionDescriptor.GetCustomAttributes(typeof(IgnoreMyCustomAttribute), false); 
     if (ignoreAttributes.Length > 0) 
      return false; 

     return true; 
    } 

shouldRun() vérifie simplement s'il y a un « IgnoreMyCustomAttribute "sur votre action. Si c'est là, votre attribut personnalisé ne fera rien.

Vous voulez maintenant créer un IgnoreMyCustomAttribute simple, qui ne fait rien:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 
public class IgnoreMyCustomAttribute: ActionFilterAttribute 
{ 
} 

Chaque fois que vous décorez votre action de contrôleur avec [IgnoreMyCustom], puis MyCustomAttribute ne feront rien. par exemple:

[IgnoreMyCustom] 
public ViewResult MyAction() { 
}