Атрибуты MVC для контроллеров и действий - PullRequest
6 голосов
/ 13 мая 2009

Есть ли способ добавить атрибут на уровне контроллера, но не на конкретное действие. Например, скажите, если у меня в контроллере было 10 действий, и только для одного из этих действий не требуется определенный атрибут, который я создал.

[MyAttribute]
public class MyController : Controller
{
    public ActionResult Action1() {}
    public ActionResult Action2() {}

    [Remove_MyAttribute]
    public ActionResult Action3() {}
}

Я мог бы потенциально переместить это Действие в другой контроллер (но это не так), или я мог бы применить MyAttribute ко всем действиям, кроме Action3, но просто подумал, есть ли более простой способ?

Ответы [ 5 ]

5 голосов
/ 07 марта 2013

Я знаю, что мой ответ немного запоздал (почти четыре года) к игре, но я наткнулся на этот вопрос и хотел поделиться разработанным мною решением, которое позволяет мне делать в значительной степени то, что хотел сделать первоначальный вопрос, в если это поможет кому-то еще в будущем.

Решение включает в себя небольшой гем под названием AttributeUsage, который позволяет нам указать атрибут на контроллере (и даже на любых базовых контроллерах!), А затем при необходимости переопределить (игнорировать / удалить) отдельные действия или субконтроллеры. Они будут «каскадно» идти туда, где на самом деле срабатывает только самый гранулированный атрибут: то есть они переходят от наименее специфичных (базовые контроллеры) к более специфичным (производные контроллеры) и наиболее специфичным (методы действия).

Вот как:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, Inherited=true, AllowMultiple=false)]
public class MyCustomFilterAttribute : ActionFilterAttribute
{

    private MyCustomFilterMode _Mode = MyCustomFilterMode.Respect;        // this is the default, so don't always have to specify

    public MyCustomFilterAttribute()
    {
    }
    public MyCustomFilterAttribute(MyCustomFilterMode mode)
    {
        _Mode = mode;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (_Mode == MyCustomFilterMode.Ignore)
        {
            return;
        }

        // Otherwise, respect the attribute and work your magic here!
        //
        //
        //
    }

}

public enum MyCustomFilterMode
{
    Ignore = 0,
    Respect = 1
}

(Я слышал, что вы любите атрибуты, поэтому я поместил некоторые атрибуты в атрибут! Это действительно то, что заставляет магию работать здесь на самом верху: разрешать им наследовать / каскадировать, но разрешать выполнять только одному из них.)

Вот как это используется сейчас:

[MyCustomFilter]
public class MyBaseController : Controller
{
    // I am the application's base controller with the filter,
    // so any derived controllers will ALSO get the filter (unless they override/Ignore)
}

public class HomeController : MyBaseController
{
    // Since I derive from MyBaseController,
    // all of my action methods will also get the filter,
    // unless they specify otherwise!

    public ActionResult FilteredAction1...
    public ActionResult FilteredAction2...

    [MyCustomFilter(Ignore)]
    public ActionResult MyIgnoredAction...    // I am ignoring the filter!

}

[MyCustomFilter(Ignore)]
public class SomeSpecialCaseController : MyBaseController
{
    // Even though I also derive from MyBaseController, I can choose
    // to "opt out" and indicate for everything to be ignored

    public ActionResult IgnoredAction1...
    public ActionResult IgnoredAction2...

    // Whoops! I guess I do need the filter on just one little method here:
    [MyCustomFilter]
    public ActionResult FilteredAction1...

}

Я надеюсь, что это скомпилировано, я вытащил его из какого-то похожего кода и сделал небольшой поиск и замену, чтобы он не был идеальным.

3 голосов
/ 13 мая 2009

Вы должны переопределить / расширить атрибут по умолчанию и добавить собственный конструктор, чтобы разрешить исключение. Или вы можете создать свой собственный атрибут для исключения (в вашем примере это [Remove_MyAttribute]).

3 голосов
/ 13 мая 2009

Йоханнес дал правильное решение, и вот как я его кодировал ... надеюсь, это поможет другим людям.

[MyFilter("MyAction")]
public class HomeController : Controller
{
    public ActionResult Action1...
    public ActionResult Action2...
    public ActionResult MyAction...
}

public class CompressFilter : ActionFilterAttribute
{
    private IList _ExcludeActions = null;

    public CompressFilter()
    {
        _ExcludeActions = new List();
    }

    public CompressFilter(string excludeActions)
    {
        _ExcludeActions = new List(excludeActions.Split(','));
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpRequestBase request = filterContext.HttpContext.Request;

        string currentActionName = (string)filterContext.RouteData.Values["action"];

        if (_ExcludeActions.Contains(currentActionName))
            return;

        ...
    }
2 голосов
/ 14 мая 2009

Обычный шаблон для того, что вы пытаетесь сделать, это иметь и атрибут с логическим параметром, который указывает, применяется атрибут или нет.

Ex:

[ComVisible] which is equivalent with [ComVisible(true)]

or 

[ComVisible(false)]

В вашем случае вы бы получили:

[MyAttribute] // defaults to true

and

[MyAttribute(false)] for applying the attribute on excluded members
2 голосов
/ 13 мая 2009

Вы можете исключить конкретное действие, передав его основному атрибуту:

 [MyAttribute(Exclude="Action3")]

EDIT

Мой пример был взят из головы (как вы можете видеть ниже, это VB.NET, может, именно здесь все пошло не так), вот как я реализовал:

<Models.MyAttribute(Exclude:="Action3")> _
Public Class MyController
Inherits System.Web.Mvc.Controller

End Class
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...