Подтвердить что ты не робот

Как проверить, имеет ли метод атрибут

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

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. Теперь, как я могу сделать этот общий способ таким образом, что он будет принимать строки и классы в качестве параметров?

4b9b3361

Ответ 1

Проблема с вашим кодом является сигнатурой 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.

Ответ 2

Существует более легкое решение, доступное по сравнению с другими выше, с текущей версией .NET/С# (4.6.1, С# 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, предоставленные другими ответами здесь.

Ответ 3

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

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);

Ответ 4

Найдите образец, в котором я найду методы в классе, которые имеют указанный атрибут.

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);
            }
        }

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