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

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

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候选方法上。

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2013-09-28 21:05  虔城墨客  阅读(600)  评论(0)    收藏  举报