Как проверить, имеет ли метод атрибут - PullRequest
23 голосов
/ 11 января 2012

У меня есть пример класса

public class MyClass{

    ActionResult Method1(){
        ....
    } 

    [Authorize]
    ActionResult Method2(){
       ....
    }

    [Authorize]    
    ActionResult Method3(int value){
       ....
    }

}

Теперь я хочу написать функцию, возвращающую true / false, которая может быть выполнена следующим образом

var controller = new MyClass();

Assert.IsFalse(MethodHasAuthorizeAttribute(controller.Method1));
Assert.IsTrue(MethodHasAuthorizeAttribute(controller.Method2));
Assert.IsTrue(MethodHasAuthorizeAttribute(controller.Method3));

Я дошел до точки, где

public bool MethodHasAuthorizeAttribute(Func<int, ActionResult> function)
{
    return function.Method.GetCustomAttributes(typeof(AuthorizeAttribute), false).Length > 0;
}

будет работать для Method3. Теперь, как я могу сделать это универсальным способом, чтобы он также брал строки и классы в качестве параметров?

Ответы [ 4 ]

20 голосов
/ 11 января 2012

Проблема с вашим кодом - подпись public bool MethodHasAuthorizeAttribute(Func<int, ActionResult> function).MethodHasAuthorizeAttribute может использоваться только с аргументами, соответствующими подписи указанного вами делегата.В этом случае метод возвращает ActionResult с параметром типа int.

Когда вы вызываете этот метод наподобие MethodHasAuthorizeAttribute(controller.Method3), компилятор выполняет преобразование группы методов.Это не всегда может быть желательным и может привести к неожиданным результатам (преобразования групп методов не всегда просты).Если вы попытаетесь вызвать MethodHasAuthorizeAttribute(controller.Method1), вы получите ошибку компилятора, потому что нет преобразования.

Более общее решение может быть построено с деревьями выражений и известным приемом "MethodOf".Он использует сгенерированные компилятором деревья выражений для нахождения цели вызова:

public static MethodInfo MethodOf( Expression<System.Action> expression )
{
    MethodCallExpression body = (MethodCallExpression)expression.Body;
    return body.Method;
}

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

MethodInfo method = MethodOf( () => controller.Method3( default( int ) ) );

С этимКстати, мы можем построить общую реализацию:

public static bool MethodHasAuthorizeAttribute( Expression<System.Action> expression )
{
    var method = MethodOf( expression );

    const bool includeInherited = false;
    return method.GetCustomAttributes( typeof( AuthorizeAttribute ), includeInherited ).Any();
}

Хорошо, вот для методов.Теперь, если вы хотите применить проверку атрибутов к классам или полям (я позабочусь о свойствах, потому что они на самом деле являются методами), нам нужно выполнить нашу проверку для MemberInfo, который является корнем наследования для Type, FieldInfo и MethodInfo.Это так же просто, как выделение поиска по атрибутам в отдельный метод и предоставление соответствующих методов адаптера с хорошими именами:

public static bool MethodHasAuthorizeAttribute( Expression<System.Action> expression )
{
    MemberInfo member = MethodOf( expression );
    return MemberHasAuthorizeAttribute( member );
}

public static bool TypeHasAuthorizeAttribute( Type t)
{
    return MemberHasAuthorizeAttribute( t );
}

private static bool MemberHasAuthorizeAttribute( MemberInfo member )
{
    const bool includeInherited = false;
    return member.GetCustomAttributes( typeof( AuthorizeAttribute ), includeInherited ).Any();
}

Я оставлю реализацию полей в качестве упражнения, вы можете использовать тот же прием, что и MethodOf.

5 голосов
/ 16 сентября 2016

Существует более простое решение по сравнению с другими, описанными выше, с текущей версией .NET / C # (4.6.1, C # 6):

Если у вас есть только один метод с таким именем:

var method = typeof(TestClass).GetMethods()
  .SingleOrDefault(x => x.Name == nameof(TestClass.TestMethod));

var attribute = method?.GetCustomAttributes(typeof(MethodAttribute), true)
  .Single() as MethodAttribute;

Теперь, чтобы проверить, установлен ли атрибут для метода:

bool isDefined = attribute != null;

И если вы хотите получить доступ к свойствам атрибута, вы можете сделать это так просто:

var someInfo = attribute.SomeMethodInfo

Если существует несколько методов с одинаковым именем, вы можете продолжить и использовать method.GetParameters() и проверить параметры вместо .GetMethods().Single...

Если вы знаете, что вашметод не имеет параметров, эта проверка проста:

var method = typeof(TestClass).GetMethods()
    .SingleOrDefault(
      x => x.Name == nameof(TestClass.TestMethod) 
      && x.GetParameters().Length == 0
);

Если нет, то это будет более сложно (проверка параметров и т. д.), а другие решения намного проще и надежнее в использовании.

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

2 голосов
/ 09 июля 2013

Я делаю что-то в этом роде:

public static bool MethodHasAuthorizeAttribute(this Delegate pMethod, string pRoleAccess)
{
    var mi = pMethod.GetMethodInfo();
    const bool includeInherited = false;
    var atr = mi.GetCustomAttributes(typeof(AuthorizeAttribute), includeInherited)
                .Select(t => (AuthorizeAttribute)t)
                .Where(t => pRoleAccess.Length>0?t.Roles == pRoleAccess:true);
    if (pRoleAccess == String.Empty)
    {
        return !atr.Any();
    }
    else
    {
        return atr.Any();
    }
}

public static bool MethodHasAllowAnonymousAttribute(this Delegate pMethod)
{
    var mi = pMethod.GetMethodInfo();
    const bool includeInherited = false;
    var atr = mi.GetCustomAttributes(typeof(AllowAnonymousAttribute), includeInherited);
    return atr.Any();
}

Вызываю это следующим образом

Func<string,System.Web.Mvc.ActionResult> func = controller.Login;
bool atrAuthorize = func.MethodHasAuthorizeAttribute(String.Empty);
0 голосов
/ 11 января 2012

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

private static void GetMethodInfo(object className)
        {
            var methods = className.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);

            foreach(var m in methods)
            {
                var parameters = m.GetParameters();
                var att = m.GetCustomAttributes(typeof (CustomAttribute), true);
            }
        }

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

...