在MVC開發中常常會看到[HttpPost],[HttpGet],也許咱們知道它是幹什麼用的但可能不知道它是怎樣實現的,爲了知道它的實現過程,我和你們一塊兒一探究竟!數組
在Visual Studio中,隨便找來一段代碼,ide
使用F12定位它,發現是個位於System.Web.Mvc中的一個類HttpPostAttribute,
它繼承於ActionMethodSelectorAttribute,
如今咱們打開反編譯工具打開Bin目錄中的System.Web.Mvc.dll,
找到查看HttpPostAttribute的代碼,函數
工具:工具
JetBrains DotPeekthis
.NET Reflectorspa
ILSpycode
HttpVerbs是什麼?
首先說一下,下面會用到:它是一個枚舉對象
public enum HttpVerbs { Get = 1, Post = 2, Put = 4, Delete = 8, Head = 16, Patch = 32, Options = 64, }
using System.Reflection; namespace System.Web.Mvc { // 定義了一個標記 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class HttpPostAttribute : ActionMethodSelectorAttribute { // 建立AcceptVerbsAttribute對象並傳入了HttpVerbs.Post, private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Post); // 此處是重寫父類的IsValidForRequest方法,但只是調用了AcceptVerbsAttribute的IsValidForRequest方法 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { return HttpPostAttribute._innerAttribute.IsValidForRequest(controllerContext, methodInfo); } } }
override重寫了父類的IsValidForRequest方法,而裏面調用了AcceptVerbsAttribute的IsValidForRequest方法。blog
AcceptVerbsAttribute是什麼?繼承
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Web.Mvc.Properties; namespace System.Web.Mvc { // 定義了一個標記 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] // 繼承於 ActionMethodSelectorAttribute public sealed class AcceptVerbsAttribute : ActionMethodSelectorAttribute { // 聲明瞭一個集合型的Verbs public ICollection<string> Verbs { get; private set; } // 第一個構造函數 // 尾部直接調用了本身類中的EnumToArray函數傳入了前面參數verbs public AcceptVerbsAttribute(HttpVerbs verbs) : this(AcceptVerbsAttribute.EnumToArray(verbs)) { } // 第二個構造函數 // 參數是無限大的string數組verbs // 它和上個構造方法不一樣的是它指定了某個或多個Verbs,而不是默認的所有(GET/POST/PUT/DELETE等) public AcceptVerbsAttribute(params string[] verbs) { if (verbs == null || verbs.Length == 0) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "verbs"); } // 建立一個只可讀的集合,並將參數verbs帶入 this.Verbs = new ReadOnlyCollection<string>(verbs); } // 若是verbs沒有match,就將match加入到verbList中 private static void AddEntryToList(HttpVerbs verbs, HttpVerbs match, List<string> verbList, string entryText) { if ((verbs & match) != (HttpVerbs)0) { verbList.Add(entryText); } } // 將verbs填入N個HttpMethod並返回個數組 // 這段代碼乾的事就是將GET,POST,PUT之類的添加到默認的Verbs之中, internal static string[] EnumToArray(HttpVerbs verbs) { List<string> list = new List<string>(); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Get, list, "GET"); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Post, list, "POST"); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Put, list, "PUT"); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Delete, list, "DELETE"); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Head, list, "HEAD"); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Patch, list, "PATCH"); AcceptVerbsAttribute.AddEntryToList(verbs, HttpVerbs.Options, list, "OPTIONS"); return list.ToArray(); } // 主要的方法! // 該方法重寫了父類IsValidForRequest方法 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } string httpMethodOverride = controllerContext.HttpContext.Request.GetHttpMethodOverride(); // 看看Verbs中是否包含從HttpContext獲取到的HttpMethod,返回bool return this.Verbs.Contains(httpMethodOverride, StringComparer.OrdinalIgnoreCase); } } }
咱們再來看它的最重要的方法:
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { if (controllerContext == null) throw new ArgumentNullException("controllerContext"); return this.Verbs.Contains<string>(controllerContext.HttpContext.Request.GetHttpMethodOverride(), (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase); }
這也就是能夠被子類被重寫的方法了!
它將你HTTP請求中的方法GET/POST/PUT等帶入到Verbs集合中看看是否存在,return的是bool類型表明有沒有。
固然它本身還繼承了一個類:
public sealed class AcceptVerbsAttribute : ActionMethodSelectorAttribute
發現這個類也繼承於ActionMethodSelectorAttribute,
你或許想起來了HttpPostAttribute也繼承了ActionMethodSelectorAttribute,
using System.Reflection; namespace System.Web.Mvc { // 註解標記 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public abstract class ActionMethodSelectorAttribute : Attribute { // 虛方法 public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo); } }
提供了一個虛方法IsValidForRequest,可以讓子類去實現。
咱們的HttpPostAttribute調用了它的AcceptVerbsAttribute中的IsValidForRequest,
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { if (controllerContext == null) throw new ArgumentNullException("controllerContext"); return this.Verbs.Contains<string>(controllerContext.HttpContext.Request.GetHttpMethodOverride(), (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase); }
建立該類AcceptVerbsAttribute的方法是用了第二種構造函數
public AcceptVerbsAttribute(params string[] verbs)
而類中的IsValidForRequest方法是判斷HttpMethod是否存在與HttpContext中,有就返回true,沒有返回false。
你也許會問我這個HttpPostAttribute到底在哪裏被調用了,爲何我掛上[HttpPost]以後它就能只容許Post訪問呢?
咱們知道HttpPostAttribute重寫了父類ActionMethodSelectorAttribute的IsValidForRequest方法,
在重寫中調用了AcceptVerbsAttribute的IsValidForRequest方法,
因此咱們用反編譯工具跟蹤誰調用了它(IsValidForRequest)便可!
根據反編譯結果,獲得類ActionMethodSelectorBase中的調用了它
protected static bool IsValidMethodSelector(ReadOnlyCollection<ActionMethodSelectorAttribute> attributes, ControllerContext controllerContext, MethodInfo method) { int count = attributes.Count; for (int index = 0; index < count; ++index) { // 注意看這裏: if (!attributes[index].IsValidForRequest(controllerContext, method)) return false; } return true; }
再深的層次我就不跟蹤了,可是你們要明白一個道理,
有Attribute標記的地方就必定會有Reflection反射,反射能夠獲取到方法附加的Attribute,而且保存到MethodInfo中,在MVC中的Controller、Action相關的類中,都會用到反射來獲取Attribute標記。
在咱們此次分析中,只要繼承於ActionMethodSelectorAttribute的標記類中實現IsValidForRequest都會經歷在ActionMethodSelectorBase調用。
因此這也就是HttpPost、HttpGet和其它標記的實現過程。