我有一个示例类
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。现在我怎么能以一种将字符串和类作为参数的方式来做这个泛型呢?
答案 0 :(得分:20)
您的代码问题是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
是{{1的继承根},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相同的技巧。
答案 1 :(得分:5)
与上述其他解决方案相比,使用当前的.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 :(得分:2)
我这样做:
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);
答案 3 :(得分:0)
找到一些示例,其中我在类中找到了应用了指定属性的方法。
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);
}
}
传递的参数是类的实例。您可以修改代码以满足您的要求,这应该非常简单。
答案 4 :(得分:0)
如果您使用FluentAssertions,则可以执行以下操作:
var classInstance = new MyClass();
Func<ActionResult> method1 = classInstance.Method1;
method1.GetMethodInfo().Should().BeDecoratedWith<AuthorizeAttribute>();
Func<ActionResult> method2 = classInstance.Method2;
method2.GetMethodInfo().Should().BeDecoratedWith<AuthorizeAttribute>();
Func<int, ActionResult> method3 = classInstance.Method3;
method3.GetMethodInfo().Should().BeDecoratedWith<AuthorizeAttribute>();