ControllerDescriptor、ActionDescriptor与ParameterDescriptor
首先我们打开ControllerDesciptor,

1 public abstract class ControllerDescriptor : ICustomAttributeProvider, IUniquelyIdentifiable { 2 3 private readonly Lazy<string> _uniqueId; //这是ControllerDescriptor的唯一标识,Lazy提供延迟对初始化的支持 4 5 protected ControllerDescriptor() { 6 _uniqueId = new Lazy<string>(CreateUniqueId); //首先在构造函数中生成一个唯一标识 7 8 9 public virtual string ControllerName { 10 get { 11 string typeName = ControllerType.Name; 12 if (typeName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) { 13 return typeName.Substring(0, typeName.Length - "Controller".Length); 14 } 15 //根据传递的类型名称,截取掉后面部分"Controller"字符后生成的就是Controller名称了。 16 return typeName; 17 } 18 } 19 20 public abstract Type ControllerType { //控制器类型 21 get; 22 } 23 24 [SuppressMessage("Microsoft.Security", "CA2119:SealMethodsThatSatisfyPrivateInterfaces", Justification = "This is overridden elsewhere in System.Web.Mvc")] 25 public virtual string UniqueId { 26 get { 27 return _uniqueId.Value; 28 } 29 } 30 31 private string CreateUniqueId() { 32 return DescriptorUtil.CreateUniqueId(GetType(), ControllerName, ControllerType); //创建唯一标识符 33 } 34 35 public abstract ActionDescriptor FindAction(ControllerContext controllerContext, string actionName);//根据ActionName找到当前Action的ActionDescriptor 36 37 public abstract ActionDescriptor[] GetCanonicalActions(); //获得当前控制器下的所有ActionDescriptor 38 39 public virtual object[] GetCustomAttributes(bool inherit) { //获得所有自定义特性 40 return GetCustomAttributes(typeof(object), inherit); 41 } 42 43 public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { 44 if (attributeType == null) { 45 throw new ArgumentNullException("attributeType"); 46 } 47 48 return (object[])Array.CreateInstance(attributeType, 0); 49 } 50 51 internal virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache) { //获得控制器下所有的过滤器 52 return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>(); 53 } 54 55 public virtual bool IsDefined(Type attributeType, bool inherit) { //判断当前控制器是否指定自定义特性 56 if (attributeType == null) { 57 throw new ArgumentNullException("attributeType"); 58 } 59 60 return false; 61 } 62 63 }
ControllerDescriptor实现了ICustomAttributeProvider和IUniquelyIdentifiable接口。
ICustomAttribute接口拥有三个方法:
1 public interface ICustomAttributeProvider 2 { 3 object[] GetCustomAttributes(bool inherit); 4 object[] GetCustomAttributes(Type attributeType, bool inherit); 5 bool IsDefined(Type attributeType, bool inherit); 6 }
IUniquelyIdentifiable只有一个UniqueId属性,这个属性表示ControllerDescriptor的唯一标识。
1 internal interface IUniquelyIdentifiable { 2 string UniqueId { get; } 3 }
除了实现的接口方法之外,ControllerDescriptor 还包含了:
FindAction方法是一个根据指定的ActionName获取对应的ActionDescriptor,ActionDescriptor我们下面就会详细了解。
GetFilterAttributes方法是一个用于获取应用在当前Controller类型上相应的特性。
GetCanonicalActions方法是获取定义在当前Controller上的所有的Action的ActionDescriptor数组。
除此之外还定义了两个属性,ControllerName和ControllerType,分别用于表示当前Controller的名称和类型。
ControllerDescriptor首先是通过Lazy延迟初始化数值,调用CreateUniqueId方法,创建一个唯一标识符。
由于ControllerDescriptor本身并没有通过反射来得到相应的特性,所以GetCustomAttributes返回的数组也为空数组对象,IsDefined方法返回false。
接下来我们要提到的就是对ControllerDescriptor的GetCustomAttributes和GetFilterAttributes方法要实现的一个重要的类型。
该类型是ControllerDescriptor的一个唯一继承者,那就是ReflectedControllerDescriptor。
我们首先来看源码。

1 public class ReflectedControllerDescriptor : ControllerDescriptor { 2 3 4 public sealed override Type ControllerType {get; 5 } 6 7 public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) { 8 9 } 10 11 private MethodInfo[] GetAllActionMethodsFromSelector() { 12 13 } 14 15 public override ActionDescriptor[] GetCanonicalActions() 16 17 public override object[] GetCustomAttributes(bool inherit) 18 19 public override object[] GetCustomAttributes(Type attributeType, bool inherit) 20 21 internal override IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache) 22 23 public override bool IsDefined(Type attributeType, bool inherit) 24 25 private ActionDescriptor[] LazilyFetchCanonicalActionsCollection() 26 27 }
ReflectedControllerDescriptor分别重写了ControllerDescriptor的GetCustomAttributes以及IsDefined方法。
在这里我们重点关注一下所重写的FindAction方法。
FindAction方法:在这里的FindAction方法是调用了ActionMethodSelect类型的FindActionMethod方法。
1 public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) { 2 //获得所有标注有ActionName特性的方法 3 List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName); 4 //根据键值为actionName 添加到未标注Http方法的特性中 5 methodsMatchingName.AddRange(NonAliasedMethods[actionName]); 6 //返回最后生成的特性方法 7 List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName); 8 9 switch (finalMethods.Count) { 10 case 0: //0个匹配,返回404 11 return null; 12 13 case 1: 14 return finalMethods[0]; //返回第一个匹配 15 16 default: 17 throw CreateAmbiguousMatchException(finalMethods, actionName); //多个匹配,报错。 18 } 19 }
此方法首先获取添加获得所有标注有ActionName特性的方法,根据键值为actionName 添加到未标注Http方法的特性中,返回最后生成的特性方法,判断返回的方法个数,返回0,说明没有匹配成功,直接返回404,若返回1个,说明匹配成功,直接返回方法数组的第一个数据,若匹配多个,则直接报错。我们看到最终方法返回的是一个MethodInfo对象。通过传递该对象,我们看到ReflectedControllerDescriptor的FindAction最终返回的就是ReflectedActionDescriptor 对象。
那么我们再来看看ActionNameAttrbitue.
public sealed class ActionNameAttribute : ActionNameSelectorAttribute { public ActionNameAttribute(string name) { } public string Name { get; private set; } public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) { return String.Equals(actionName, Name, StringComparison.OrdinalIgnoreCase); } }
该特性重写了IsValidName方法,我们可以看到只是简单的验证了Action名称是否与指定的一致。
很多人或许会把ActionSelectorAttribute和ActionMethodSelectorAttribute混淆,两者都具有验证筛选的方法,只不过前者是针对Action名称做筛选,而后者的抽象方法IsValidForRequest判断目标Action方法是否与当前的请求相匹配。
1 public abstract class ActionMethodSelectorAttribute : Attribute { 2 public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo); 3 }
MVC定义了7个HTTP方法,分别是:GET、POST、PUT、DELETE、Head、Options和Patch的ActionMethodSelectorAttribute。
当被应用在Action方法中,只有当前请求的HTTP方法与之匹配的情况下目标Action才会被选择。
除了定义的这7种Http方法外,Http还定义了另一个名为AcceptVerbsAttribute的特性,我们来看看:
1 public sealed class AcceptVerbsAttribute : ActionMethodSelectorAttribute { 2 public AcceptVerbsAttribute(HttpVerbs verbs) 3 : this(EnumToArray(verbs)) { 4 } 6 public AcceptVerbsAttribute(params string[] verbs) { 8 } 10 public ICollection<string> Verbs { 11 get; 12 private set; 13 } 14 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { 16 } 17 }
AcceptVerbsAttribute拥有两个构造函数,传值可以是多个或单个HttpVerbs的枚举类型,也可以是单个或多个字符串,通过字符串指定的Http方法是不区分大小写的。
IsValidForRequest方法首先根据ControllerContext获得当前的Http方法,再在所有的Http方法中检索是否存在其中。
2 public enum HttpVerbs { 3 Get = 1 << 0, 4 Post = 1 << 1, 5 Put = 1 << 2, 6 Delete = 1 << 3, 7 Head = 1 << 4 8 }
除了以上,MVC还定义了一个NonActionAttribute特性。
1 public sealed class NonActionAttribute : ActionMethodSelectorAttribute { 2 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { 3 return false; 4 } 5 }
我们看到IsValidForRequest方法直接返回false,说明标注此特性,将不会被作为是一个Action方法,直接排除在Action候选方法上。