ASP.NET MVC中的[HttpPost]是什麼呢?

[HttpPost]是什麼呢?


在MVC開發中常常會看到[HttpPost][HttpGet],也許咱們知道它是幹什麼用的但可能不知道它是怎樣實現的,爲了知道它的實現過程,我和你們一塊兒一探究竟!數組

在Visual Studio中,隨便找來一段代碼,ide

clipboard.png

使用F12定位它,發現是個位於System.Web.Mvc中的一個類HttpPostAttribute
它繼承於ActionMethodSelectorAttribute
如今咱們打開反編譯工具打開Bin目錄中的System.Web.Mvc.dll
找到查看HttpPostAttribute的代碼,函數

工具:工具

  • JetBrains DotPeekthis

  • .NET Reflectorspa

  • ILSpycode

開始分析


HttpVerbs是什麼
首先說一下,下面會用到:它是一個枚舉對象

HttpVerbs.cs

public enum HttpVerbs
{
    Get = 1,
    Post = 2,
    Put = 4,
    Delete = 8,
    Head = 16,
    Patch = 32,
    Options = 64,
}

HttpPostAttribute.cs

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方法,而裏面調用了AcceptVerbsAttributeIsValidForRequest方法。blog

AcceptVerbsAttribute是什麼?繼承

AcceptVerbsAttribute.cs

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

ActionMethodSelectorAttribute.cs

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重寫了父類ActionMethodSelectorAttributeIsValidForRequest方法,
在重寫中調用了AcceptVerbsAttributeIsValidForRequest方法,
因此咱們用反編譯工具跟蹤誰調用了它(IsValidForRequest)便可!

clipboard.png

根據反編譯結果,獲得類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和其它標記的實現過程。

相關文章
相關標籤/搜索