在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和其它标记的实现过程。