ASP.NET MVC三个重要的描述对象:ActionDescriptor

在Model绑定过程中会通过激活的Controller类型创建用于描述它的ControllerDescriptor对象。Controller是一组Action方法的集合,而每一个Action通过ActionDescriptor对象来表示,在这篇文章中我们就来着重谈谈不同类型的ActionDescriptor。[本文已经同步到《How ASP.NET MVC Works?》中]

目录 一、ActionDescriptor 二、AsyncActionDescriptor 三、ReflectedActionDescriptor 四、ReflectedAsyncActionDescriptor 五、TaskAsyncActionDescriptor

一、ActionDescriptor

用于描述定义在Controller类中的Action方法的ActionDescriptor定义如下。属性ActionName和ControllerDescriptor表示Action的名称和描述所在Controller的ControllerDescriptor对象。表示唯一标识的UniqueId属性由自身类型、Controller的类型与Action名称三者派生。

   1: public abstract class ActionDescriptor : ICustomAttributeProvider
   2: {
   3:     public virtual object[] GetCustomAttributes(bool inherit);
   4:     public virtual object[] GetCustomAttributes(Type attributeType,  bool inherit);
   5:     public virtual bool IsDefined(Type attributeType, bool inherit);
   6:     public virtual IEnumerable<FilterAttribute> GetFilterAttributes( bool useCache);
   7:     
   8:     public abstract ParameterDescriptor[] GetParameters();
   9:     public abstract object Execute(ControllerContext controllerContext,  IDictionary<string, object> parameters);
  10:     public virtual ICollection<ActionSelector> GetSelectors();
  11:     public virtual FilterInfo GetFilters();    
  12:  
  13:     public abstract string ActionName { get; }
  14:     public abstract ControllerDescriptor ControllerDescriptor { get; }
  15:     public virtual string UniqueId { get; }
  16: }

与ControllerDescriptor一样,ActionDescriptor同样实现了定义在ICustomAttributeProvider接口中的方法,我们可以通过相应的方法得到应用在Action方法上的相关特性,或者判断某个指定的特性是否应用在对应的Action方法上。GetFilterAttributes方法用于返回应用在Action方法上的所有筛选器特性。用于描述Action方法中所有参数的ParameterDescriptor数组通过方法GetParameters返回。Action方法的执行可以直接通过调用方法Execute来完成,该方法的两个参数controllerContext和parameters分别代表Action方法执行所在的Controller上下文和传入的参数。

GetSelectors方法用于返回一组表示Action选择器的类型为ActionSelector的对象,而ActionSelector是一个委托类型。如下面的代码片断所示,ActionSelector委托具有唯一的类型为ControllerContext的参数,布尔类型的返回值表示目标Action方法是否与指定的Controller上下文相匹配。该方法默认返回的是一个空的ActionSelector集合。

   1: public delegate bool ActionSelector(ControllerContext controllerContext);

ActionDescriptor的GetFilters方法返回的是一个FilterInfo类型的对象,我们通过这个对象可以得到应用在该Action方法上所有的筛选器。如下面的代码所示,FilterInfo具有四个只读的集合属性,分别代码应用在该Action方法上的四种类型的筛选器(ActionFilter、AuthorizationFilter、ExceptionFilter和ResultFilter)。

   1: public class FilterInfo
   2: {
   3:     public IList<IActionFilter>         ActionFilters { get; }
   4:     public IList<IAuthorizationFilter>  AuthorizationFilters { get; }
   5:     public IList<IExceptionFilter>      ExceptionFilters { get; }
   6:     public IList<IResultFilter>         ResultFilters { get; }
   7: }

二、AsyncActionDescriptor

异步版本的ActionDescriptor通过AsyncActionDescriptor类型表示,它用于描述定义在AsyncController中的异步方法。如下面的代码片断所示,AsyncActionDescriptor是一个继承自ActionDescriptor的抽象类,它重写了Execute方法,并且定义了两个用于异步执行Action方法的抽象方法BeginExecute/EndExecute。

   1: public abstract class AsyncActionDescriptor : ActionDescriptor
   2: {
   3:     public abstract IAsyncResult BeginExecute( ControllerContext controllerContext, IDictionary<string, object> parameters, AsyncCallback callback,  object state);
   4:     public abstract object EndExecute(IAsyncResult asyncResult);
   5:     public override object Execute(ControllerContext controllerContext,  IDictionary<string, object> parameters);
   6: }

实际上AsyncActionDescriptor重写的Execute方法并没有实现任何Action方法执行的逻辑,而是直接抛出一个InvalidOperationException异常,意味用于同步执行Action操作的Execute方法在这里无效。

三、ReflectedActionDescriptor

上面我们介绍的ReflectedControllerDescriptor的FindAction和GetCanonicalActions方法返回的ActionDescriptor对象实际上是一个ReflectedActionDescriptor对象。顾名思义,ReflectedActionDescriptor针对Action方法元数据信息的解析同样通过针对目标方法成员的反射来实现。如下面的代码片断所示,ReflectedActionDescriptor直接继承自ActionDescriptor,表示Action名称、所在Controller的描述以及Action方法的只读属性ActionName、ControllerDescriptor和MethodInfo均在构造函数中初始化。

   1: public class ReflectedActionDescriptor : ActionDescriptor
   2: {   
   3:     public ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor);    
   4:     
   5:     public override object[] GetCustomAttributes(bool inherit);
   6:     public override object[] GetCustomAttributes(Type attributeType,  bool inherit);
   7:     public override bool IsDefined(Type attributeType, bool inherit);
   8:     public override IEnumerable<FilterAttribute> GetFilterAttributes( bool useCache);
   9:  
  10:     public override ParameterDescriptor[] GetParameters();
  11:     public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);
  12:     public override ICollection<ActionSelector> GetSelectors();
  13:    
  14:     public override string ActionName { get; }
  15:     public override ControllerDescriptor ControllerDescriptor { get; }
  16:     public MethodInfo MethodInfo { get;}
  17:     public override string UniqueId { get; }
  18: }

ReflectedControllerDescriptor通过对应用在Action方法上所有特性的反射实现了定义在ICustomAttributeProvider接口中的三个方法。对于方法GetFilterAttributes返回的应用在Action方法上的筛选器特性,同样是通过相同的方式获得。

ReflectedControllerDescriptor重写了UniqueId属性,在现有的基础上将表示Action方法的MethodInfo对象作为了决定元素之一。也就是说,表示ReflectedControllerDescriptor对象唯一标识的UniqueId属性通过自身的类型、Controller类型、Action名称和表示目标Action方法的MethodInfo对象四者派生。

对于通过方法GetParameters返回的用于描述所有参数的ParameterDescriptor数组,也是通过对Action方法的参数列表进行反射来创建的。Execute方法最终传入参数列表调用MethodInfo对象执行Action方法。

ReflectedControllerDescriptor的GetSelectors返回的ActionSelector集合涉及到一个类型为ActionMethodSelectorAttribute的特性。ActionMethodSelectorAttribute特性应用于Action方法,会影响到通过ControllerDescriptor的FindAction方法实现的基于当前Controller上下文的Action方法的选择。如下面的代码片断所示,ActionMethodSelectorAttribute是一个抽象类型,其唯一的抽象方法IsValidForRequest用于判断目标Action方法是否与当前请求(即指定的Controller上下文)相匹配。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = false,  Inherited = true)]
   2: public abstract class ActionMethodSelectorAttribute : Attribute
   3: {
   4:     public abstract bool IsValidForRequest(ControllerContext controllerContext,  MethodInfo methodInfo);
   5: }

在ASP.NET MVC应用编程接口中定义了如下四个基于HTTP方法(GET、POST、PUT和DELETE)的ActionMethodSelectorAttribute,当我们将它们应用到某个Action方法上时,只有在当前请求的HTTP方法与之相匹配的情况下目标Action方法才会被选择。

  • System.Web.Mvc.HttpGetAttribute
  • System.Web.Mvc.HttpPostAttribute
  • System.Web.Mvc.HttpPutAttribute
  • System.Web.Mvc.HttpDeleteAttribute

除了上面四个基于某种HTTP方法的ActionMethodSelectorAttribute特性之外,还定义了一个AcceptVerbsAttribute特性。AcceptVerbsAttribute的不同之处在于它可以动态地指定一个或者多个匹配的HTTP方法。如下面的的代码片断所示,AcceptVerbsAttribute具有一个字符串集合类型的只读属性Verbs,用于表示目标Action方法支持的HTTP方法(HTTP Method又被称为HTTP Verb),该属性在构造函数中被初始化。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
   2: public sealed class AcceptVerbsAttribute : ActionMethodSelectorAttribute
   3: {    
   4:     public AcceptVerbsAttribute(HttpVerbs verbs);
   5:     public AcceptVerbsAttribute(params string[] verbs); 
   6:   
   7:     public override bool IsValidForRequest(ControllerContext controllerContext,  MethodInfo methodInfo);
   8:     public ICollection<string> Verbs { get;  }
   9: }
  10:  
  11: [Flags]
  12: public enum HttpVerbs
  13: {
  14:     Get     = 1,
  15:     Post    = 2,
  16:     Put     = 4,
  17:     Delete  = 8,
  18:     Head    = 16,
  19: }

从上面的代码片断可以看出AcceptVerbsAttribute具有两个构造函数,其参数类型分别是HttpVerbs枚举和字符串数组,由于AcceptVerbsAttribute枚举应用了FlagsAttribute特性,我们可以使用操作符“|”指定多个HTTP方法。如下所示的两种应用AcceptVerbsAttribute的方式是等效的。顺便提一下,通过字符串指定的HTTP方式是不区分大小写的;实际上述的四个ActionMethodSelectorAttribute在内部使用了AcceptVerbsAttribute特性实现了具体的Action方法选择逻辑。

   1: //使用HttpVerbs枚举表示HTTP方法
   2: public class ContactController
   3: {
   4:     [AcceptVerbs(HttpVerbs.Put|HttpVerbs.Post|HttpVerbs.Delete)]
   5:     public ActionResult UpdateContact(Contact contact)
   6:     { 
   7:         //省略实现
   8:     }
   9: }
  10:  
  11: //使用字符串表示HTTP方法
  12: public class ContactController
  13: {
  14:     [AcceptVerbs("PUT","POST","DELETE")]
  15:     public ActionResult UpdateContact(Contact contact)
  16:     {
  17:         //省略实现
  18:     }
  19: }

除了上面5个基于HTTP方法的ActionMethodSelectorAttribute特性之外,还具有另一个具有如下定义的NonActionAttribute特性。顾名思义,应用了NonActionAttribute特性的方法将不会被认为是一个Action方法,所以在根据请求进行目标Action方法选择 的时候,这样的方法总是被排除在候选范围之内,所以IsValidForRequest方法直接返回False。

   1: [AttributeUsage(AttributeTargets.Method, AllowMultiple = false,  Inherited = true)]
   2: public sealed class NonActionAttribute : ActionMethodSelectorAttribute
   3: {    
   4:     public override bool IsValidForRequest(ControllerContext controllerContext,  MethodInfo methodInfo)
   5:     {
   6:         return false;
   7:     }
   8: }

四、ReflectedAsyncActionDescriptor

异步的ReflectedControllerDescriptor由ReflectedAsyncActionDescriptor类型表示。它用于描述以XxxAsync/XxxCompleted方式定义的异步Action方法,所以一个ReflectedAsyncActionDescriptor对象通过代表着两个方法的MethodInfo对象来创建。如下面的代码片断所示,ReflectedAsyncActionDescriptor的构造的参数asyncMethodInfo和completedMethodInfo就代码这两个MethodInfo。在构造函数中初始化的这两个MethodInfo对象爱分别通过只读属性AsyncMethodInfo和CompletedMethodInfo返回。

   1: public class ReflectedAsyncActionDescriptor : AsyncActionDescriptor
   2: {    
   3:     public ReflectedAsyncActionDescriptor(MethodInfo asyncMethodInfo,  MethodInfo completedMethodInfo, string actionName,  ControllerDescriptor controllerDescriptor);
   4:     
   5:     public override IAsyncResult BeginExecute(ControllerContext controllerContext, IDictionary<string, object> parameters, AsyncCallback callback, object state);
   6:     public override object EndExecute(IAsyncResult asyncResult);
   7:  
   8:     public override object[] GetCustomAttributes(bool inherit);
   9:     public override object[] GetCustomAttributes(Type attributeType, bool inherit);
  10:     public override IEnumerable<FilterAttribute> GetFilterAttributes( bool useCache);
  11:     public override ParameterDescriptor[] GetParameters();
  12:     public override ICollection<ActionSelector> GetSelectors();
  13:     public override bool IsDefined(Type attributeType, bool inherit);
  14:  
  15:     public override string ActionName { get; }
  16:     public MethodInfo AsyncMethodInfo {  get;  }
  17:     public MethodInfo CompletedMethodInfo {  get; }
  18:     public override ControllerDescriptor ControllerDescriptor { get; }
  19:     public override string UniqueId { get; }
  20: }

ReflectedAsyncActionDescriptor方法中用于相关特性(定义在ICustomAttributeProvider接口中的三个方法,用于获取筛选器特性列表的GetFilterAttributes方法以及GetSelectors方法对ActionMethodSelectorAttribute特性的解析)和参数描述(GetParameters方法)都是通过针对XxxAsync方法(即AsyncMethodInfo属性)的反射实现的。实现的BeginExecute/EndExecute最终对AsyncMethodInfo和CompletedMethodInfo的调用实现了对Action方法的异步执行。

五、TaskAsyncActionDescriptor

异步Action除了以配对的XxxAsync/XxxCompleted方法进行定义之外,还可以通过一个返回类型为Task的方法来定义,基于后者的Action描述通过类型TaskAsyncActionDescriptor表示。如下面的代码片断所示,TaskAsyncActionDescriptor具有一个名为TaskMethodInfo的只读属性,正是表示的这个基于Task的方法,该属性在构造函数中初始化。

   1: public class TaskAsyncActionDescriptor : AsyncActionDescriptor
   2: {   
   3:     public TaskAsyncActionDescriptor(MethodInfo taskMethodInfo, string actionName, ControllerDescriptor controllerDescriptor);
   4:     
   5:     public override IAsyncResult BeginExecute(ControllerContext controllerContext, IDictionary<string, object> parameters, AsyncCallback callback, object state);
   6:     public override object EndExecute(IAsyncResult asyncResult);
   7:     public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);
   8:  
   9:     public override object[] GetCustomAttributes(bool inherit);
  10:     public override object[] GetCustomAttributes(Type attributeType, bool inherit);
  11:     public override IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache);
  12:     public override ParameterDescriptor[] GetParameters();
  13:     public override ICollection<ActionSelector> GetSelectors();
  14:     public override bool IsDefined(Type attributeType, bool inherit);
  15:  
  16:     public override string ActionName { get; }
  17:     public override ControllerDescriptor ControllerDescriptor { get; }
  18:     public MethodInfo TaskMethodInfo { get; }
  19:     public override string UniqueId { get; }
  20: }

TaskAsyncActionDescriptor对于涉及到特性和参数描述的方法都是通过针对TaskMethodInfo的反射来完成的。用于实现对Action操作的异步执行的BeginExecute/EndExecute通过Action方法返回的Task对象来完成(BeginExecute执行Action方法得到并异步执行Task,EndExecute方法获取Task执行的结果)。TaskAsyncActionDescriptor重写了Execute方法并在其中直接抛出异常。

ASP.NET MVC三个重要的描述对象:ControllerDescriptor ASP.NET MVC三个重要的描述对象:ActionDescriptor ASP.NET MVC三个重要的描述对象:ControllerDescriptor与ActionDescriptor的创建机制 ASP.NET MVC三个重要的描述对象:ParameterDescriptor

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据分析

char varchar nchar nvarcharar到底有多大区别

首先说明下,ASP.NET MVC系列还在龟速翻译中。 工作好多年,基础知识甚是薄弱,决定以后在coding(cv操作)的时候尽量多google下,然后总结下来...

31960
来自专栏Google Dart

Dart语言指南(二) 顶

Dart是一种面向对象的语言 包含类和基于 mixin 的继承两部分。每个对象是一个类的实例, 并且 Object.是所有类的父类。 基于 mixin 的继承指...

27710
来自专栏算法修养

HDU 5634 Rikka with Phi (线段树)

Problem Description Rikka and Yuta are interested in Phi function (which is kn...

314100
来自专栏黑白安全

PHP中函数和语言结构的区别

关于PHP中的函数和语言结构的区别,本文给大伙逐步分析。函数众所周知它的三要素为:函数名、参数、返回值,调用方式采用函数名加括号()的形式进行调用。语言结构可以...

13020
来自专栏一枝花算不算浪漫

[读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性

35780
来自专栏玄魂工作室

Python黑帽编程2.4 流程控制

Python黑帽编程2.4 流程控制 本节要介绍的是Python编程中和流程控制有关的关键字和相关内容。 2.4.1 IF …..ELSE 先上一段代码: ...

29040
来自专栏码云1024

python简明笔记

通过 for 语句我们可以使用 for 循环。Python 里的 for 循环与 C 语言中的不同。这里的 for 循环遍历任何序列(比如列表和字符串)中的每一...

62190
来自专栏iOS技术杂谈

Java8 Lambda表达式与Stream API (二): Stream API的使用你要知道的Java8 匿名内部类、函数式接口、lambda表达式与Stream API都在这里

你要知道的Java8 匿名内部类、函数式接口、lambda表达式与Stream API都在这里 转载请注明出处 https://cloud.tencent.co...

54760
来自专栏决胜机器学习

PHP数据结构(八) ——赫夫曼树实现字符串编解码(实践2)

PHP数据结构(八)——赫夫曼树实现字符串编解码(实践2) (原创内容,转载请注明来源,谢谢) 公众号规定不能超过3000字,只能分两篇,见谅。 由于需要分两篇...

35460
来自专栏java 成神之路

Java 对象占用内存大小

42760

扫码关注云+社区

领取腾讯云代金券