ASP.NET MVC三個重要的描述對象:ActionDescriptor
在Model綁定過程中會通過激活的Controller類型創建用於描述它的ControllerDescriptor對象。Controller是一組Action方法的集合,而每一個Action通過ActionDescriptor對象來表示,在這篇文章中我們就來著重談談不同類型的ActionDescriptor。[本文已經同步到《How ASP.NET MVC Works?》中]
目錄
一、ActionDescriptor
二、AsyncActionDescriptor
三、ReflectedActionDescriptor
四、ReflectedAsyncActionDescriptor
五、TaskAsyncActionDescriptor
用於描述定義在Controller類中的Action方法的ActionDescriptor定義如下。屬性ActionName和ControllerDescriptor表示Action的名稱和描述所在Controller的ControllerDescriptor對象。表示唯一標識的UniqueId屬性由、與三者派生。
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返回。,該方法的兩個參數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: }
異步版本的ActionDescriptor通過AsyncActionDescriptor類型表示,它用於描述定義在AsyncController中的異步方法。如下麵的代碼片斷所示,AsyncActionDescriptor是一個繼承自ActionDescriptor的抽象類,它重寫了Execute方法,並且定義了兩個用於異步執行Action方法的抽象方法。
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異常,意味用於。
上麵我們介紹的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屬性通過。
對於通過方法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: }
異步的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方法的異步執行。
異步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
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 11:34:34
上一篇:
ASP.NET MVC三個重要的描述對象:ControllerDescriptor
下一篇:
ASP.NET MVC三個重要的描述對象:ControllerDescriptor和ActionDescriptor的創建
中國企業應用數據分析大概情況和未來趨勢
HTAP數據庫 PostgreSQL 場景與性能測試之 21 - (OLTP+OLAP) 排序、建索引
互聯網企業安全高級指南1.3 互聯網企業和傳統企業在安全建設中的區別
iis安裝完成後,管理工具中不顯示,解決方案
九度題目1342:尋找最長合法括號序列II
【轉】ubuntu修改主機名
穀歌雲平台使用英特爾處理器背後 探索硬件廠商那顆“軟件”的心
求職簡曆中一些常見的問題
IComparable<>,IFormattable,IEnumerable<>接口使用
11月16日雲棲精選夜讀:阿裏雲 oss JavaScript客戶端簽名文件上傳 vue2.0