日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MVC源码分析 - Action查找和过滤器的执行时机

發布時間:2023/12/13 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MVC源码分析 - Action查找和过滤器的执行时机 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

接著上一篇, 在創建好Controller之后, 有一個 this.ExecuteCore()方法, 這部分是執行的. 那么里面具體做了些什么呢?

//ControllerBase
protected
virtual void Execute(RequestContext requestContext) {if (requestContext == null){throw new ArgumentNullException("requestContext");}if (requestContext.HttpContext == null){throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");}this.VerifyExecuteCalledOnce();
   //在這里創建了控制器上下文, ControllerContext
this.Initialize(requestContext);using (ScopeStorage.CreateTransientScope()){
     //加載 TempData, 創建及執行 Action, 處理 Action 返回的 ActionResult, 保存TempData
this.ExecuteCore();} }

來看一下這里的 ExecuteCore具體是執行的那里的方法.

//System.Web.MVC.Controller protected override void ExecuteCore() {
   //從Session 中加載 TempData 數據
this.PossiblyLoadTempData();try{
     //從路由中獲取 Action 名稱
string requiredString = this.RouteData.GetRequiredString("action");
     //判斷部分會去執行 Action, 并處理 Action 返回的ActionResult
if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString)){this.HandleUnknownAction(requiredString);}}finally{
     //保存 TempData 數據
this.PossiblySaveTempData();} }

這個類應該還是蠻熟悉的吧, 我們創建的控制器類, 都會直接或者間接繼承這個類.

?

一、解析

1. PossiblyLoadTempData()

首先來看一下這個方法吧. 看看里面做了些什么

internal void PossiblyLoadTempData() {if (!base.ControllerContext.IsChildAction){base.TempData.Load(base.ControllerContext, this.TempDataProvider);} }

看到這里的TempData, 感覺好熟悉吧. 我還是嘮叨一句吧.

TempData是保存在session中的, 可以用于不同Controller,不同Action,Action到View之間的傳值.

額, 要不要繼續解析一下呢, 算了, 看一下吧, 起碼看到這個數據是保存在session中的.

//TempDataDictionary
public
void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) {IDictionary<string, object> dictionary = tempDataProvider.LoadTempData(controllerContext);this._data = (dictionary != null) ?
    new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase) :
    new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);this._initialKeys = new HashSet<string>(this._data.Keys, StringComparer.OrdinalIgnoreCase);this._retainedKeys.Clear(); }

繼續看LoadTempData()方法, 真相就能大白了.

//System.Web.Mvc.SessionStateTempDataProvider
public
virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) {HttpSessionStateBase session = controllerContext.HttpContext.Session;if (session != null){Dictionary<string, object> dictionary = session["__ControllerTempData"] as Dictionary<string, object>;if (dictionary != null){session.Remove("__ControllerTempData");return dictionary;}}return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); }

從這里能看到, 我所言非虛了, 確實是存放在Session中的.

注意 : 同一個 TempData 只能被傳遞一次, 當在Session中找到TempData后, 就會將它清除掉, 下一次請求是不能再獲取到這個數據. 因為在Session中已經被清除了.

?

2.?GetRequiredString("action")

這個方法應該不需要多說了, 跟前面獲取控制器名稱的方式一樣, 只不過這里是用來獲取 Action 方法的名稱

?

3.?InvokeAction 這個是重點方法

前面既然已經獲取到 Action 方法的名字, 那么現在是不是應該去找到這個方法, 并看看是否能匹配的上呢? 匹配的時候, 用的是哪一些條件? 如果匹配的上, 又怎么執行的呢? 謎底即將揭曉.

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) {if (controllerContext == null){throw new ArgumentNullException("controllerContext");}if (string.IsNullOrEmpty(actionName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");}
   //根據控制器上下文, 來獲取控制器描述對象ControllerDescriptor controllerDescriptor
= this.GetControllerDescriptor(controllerContext);
   //這里調用的FindAction方法, 其實就是調用控制器描述類里面的 FindAction 方法, 但是這是一個抽象方法
   //這里返回的是 Action 方法的描述信息
   //默認調用的是 System.Web.Mvc.ReflectedControllerDescriptor 的 FindAction 方法(這里說的都是同步的情況下)ActionDescriptor actionDescriptor
= this.FindAction(controllerContext, controllerDescriptor, actionName);if (actionDescriptor == null){return false;}
   //獲取所有的過濾器FilterInfo filters
= this.GetFilters(controllerContext, actionDescriptor);try{
     //Authorization 過濾器, 執行之后,會將結果存放在 ActionResult 類型的Result屬性中,
     //如果返回結果不為空, 則不會再去執行Action里面的方法和View里面的內容了.
AuthorizationContext context
= this.InvokeAuthorizationFilters(controllerContext,
        filters.AuthorizationFilters, actionDescriptor);
if (context.Result != null){this.InvokeActionResult(controllerContext, context.Result);}else{if (controllerContext.Controller.ValidateRequest){ValidateRequest(controllerContext);}
        //獲取參數的信息, 存入字典中, 以供做參數匹配IDictionary
<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
        //Action 過濾器, Action方法的執行也在這里面進行ActionExecutedContext context2
= this.InvokeActionMethodWithFilters(controllerContext,
          filters.ActionFilters, actionDescriptor, parameterValues);
        //Result 過濾器
this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);}}catch (ThreadAbortException){throw;}catch (Exception exception){
     //錯誤信息過濾器, HandleErrorExceptionContext context3
= this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);if (!context3.ExceptionHandled){throw;}this.InvokeActionResult(controllerContext, context3.Result);}return true; }

乍一看, 里面的內容真心多啊. 還是那句話, 不要怕, 一個一個來.

3.1 FindAction - 先看一下這個方法, 是怎么找到 Action 的

//ReflectedControllerDescriptor
public
override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) {if (controllerContext == null){throw new ArgumentNullException("controllerContext");}if (string.IsNullOrEmpty(actionName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");}MethodInfo methodInfo = this._selector.FindActionMethod(controllerContext, actionName);if (methodInfo == null){return null;}
   //ReflectedActionDescriptor 類是 繼承自 ActionDescriptor類的
return new ReflectedActionDescriptor(methodInfo, actionName, this); }

從這里看, 是通過控制器上下文和Action方法的名稱去獲取到的.

那么進去再看一下吧.

public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) {List<MethodInfo> matchingAliasedMethods = this.GetMatchingAliasedMethods(controllerContext, actionName);matchingAliasedMethods.AddRange(this.NonAliasedMethods[actionName]);List<MethodInfo> ambiguousMethods = RunSelectionFilters(controllerContext, matchingAliasedMethods);switch (ambiguousMethods.Count){case 0:return null;case 1:return ambiguousMethods[0];}throw this.CreateAmbiguousMatchException(ambiguousMethods, actionName); }

3.1.1 GetMatchingAliaseMethods / NonAliaseMethods

這里可能有點讓人費解, 可能是不明白 Aliased 的意思, 翻譯過來其實是別名的意思.

這里牽涉到Action的一個功能, 就是給Action取別名. 通過 ActionNameAttribute 特性來進行.

這里的意思, 就是獲取到所有的方法, 包括有別名的和沒有別名的. 對于聲明了 NonAliasedAttribute特性的方法, 也會獲取出來.

3.1.2 RunSelectionFilters

private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) {List<MethodInfo> list = new List<MethodInfo>();List<MethodInfo> list2 = new List<MethodInfo>();using (List<MethodInfo>.Enumerator enumerator = methodInfos.GetEnumerator()){Func<ActionMethodSelectorAttribute, bool> predicate = null;MethodInfo methodInfo;while (enumerator.MoveNext()){methodInfo = enumerator.Current;ICollection<ActionMethodSelectorAttribute> actionMethodSelectorAttributes =
            ReflectedAttributeCache.GetActionMethodSelectorAttributes(methodInfo);
if (actionMethodSelectorAttributes.Count == 0){list2.Add(methodInfo);}else{if (predicate == null){predicate = attr => attr.IsValidForRequest(controllerContext, methodInfo);}if (actionMethodSelectorAttributes.All<ActionMethodSelectorAttribute>(predicate)){list.Add(methodInfo);}}}}if (list.Count <= 0){return list2;}return list; }

這里其實是對方法進行一個過濾和一個返回優先級的問題.

Action上的Attribute如果是ActionMethodSelectorAttribute類型或者是繼承了它的, 并且該特性的 IsValidForRequest()返回的結果是true,那么就會通過篩選, 優先返回.

這里其實主要是為了過濾 AcceptVerbsAttributes 和 NonActionAttribute 的.

NonActionAttribute : 他的IsValidForRequest()返回的是false, 所以Action上有此特性的方法會被篩選掉.

AcceptVerbsAttributes ?: 同名方法, 必須聲明不同的此特性, post, get, 否則也還是會報錯.

3.2 GetFilters() - 獲取Controller 和 Action 中, 聲明的所有的 過濾器

protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {return new FilterInfo(this._getFiltersThunk(controllerContext, actionDescriptor)); }

這里的_getFiltersThunk是一個Func<>()委托, 那么具體是什么方法呢?

其實這里指向的是System.Web.Mvc.FilterProviderCollection的GetFilters()方法, 別問我是怎么知道的哦.?

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {if (controllerContext == null){throw new ArgumentNullException("controllerContext");}if (actionDescriptor == null){throw new ArgumentNullException("actionDescriptor");}IEnumerable<Filter> source = (from fp in this.CombinedItems
      select fp.GetFilters(controllerContext, actionDescriptor))
        .OrderBy<Filter, Filter>(filter => filter, _filterComparer);return this.RemoveDuplicates(source.Reverse<Filter>()).Reverse<Filter>(); }

看一下這里的GetFilters(), 他其實是IFilterProvider接口中的方法, 那么哪一些類實現了這個接口呢?

解析到這里, 大致已經能知道, 獲取了哪一些過濾器了.

在更進一步, 看一下 ControllerInstanceFilterProvider里面, 干了些什么.

?

public enum FilterScope {Action = 30,Controller = 20,First = 0,Global = 10,Last = 100 }public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {if (controllerContext.Controller == null){yield break;}yield return new Filter(controllerContext.Controller, FilterScope.First, -2147483648); }

?

3.3?InvokeAuthorizationFilters() -?Authorization過濾器, 控制器的部分下一篇會詳細描述

protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext,
    IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor) {AuthorizationContext filterContext = new AuthorizationContext(controllerContext, actionDescriptor);foreach (IAuthorizationFilter filter in filters){
     //遍歷執行控制器方法filter.OnAuthorization(filterContext);
if (filterContext.Result != null){return filterContext;}}return filterContext; }

3.4?InvokeActionMethodWithFilters() - Action 過濾器, 這里面其實會執行兩個過濾器 : OnActionExecuting / OnActionExecuted

我們其實都知道, 這兩個過濾器中間, 還少了一個東西, 就是 Action 方法的執行, 那么在執行這個方法的過程之中, 就會去執行 Action 方法

protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext,
  IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);Func<ActionExecutedContext> seed = () => new ActionExecutedContext(controllerContext, actionDescriptor, false, null)
    { Result = this.InvokeActionMethod(controllerContext, actionDescriptor, parameters) };return filters.Reverse<IActionFilter>().Aggregate<IActionFilter,
    Func<ActionExecutedContext>>(seed, (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next))(); }

方法

參數

描述

OnActionExecuting

ActionExecutingContext

在行為方法執行前執行

OnActionExecuted

ActionExecutedContext

在行為方法執行后執行

OnResultExecuting

ResultExecutingContext

在行為方法返回前執行

OnResultExecuted

ResultExecutedContext

在行為方法返回后執行

?

過濾器這部分內容會在后面的篇章做出詳細解釋.

3.5?InvokeActionResultWithFilters() - Result 過濾器, 這里面也會執行兩個過濾器 : OnResultExecuting / OnResultExecuted

這里同上面是一樣的, 這兩個過濾器中間, 少了一個 View 的執行, 也就是說, 在執行這個方法的時候, 會動態執行 View 頁面方法.

protected virtual ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext,
  IList<IResultFilter> filters, ActionResult actionResult) {ResultExecutingContext preContext = new ResultExecutingContext(controllerContext, actionResult);Func<ResultExecutedContext> seed = delegate {this.InvokeActionResult(controllerContext, actionResult);return new ResultExecutedContext(controllerContext, actionResult, false, null);};return filters.Reverse<IResultFilter>().Aggregate<IResultFilter,
    Func<ResultExecutedContext>>(seed, (next, filter) => () => InvokeActionResultFilter(filter, preContext, next))(); }

3.6 ?InvokeActionResult()

注意到這里還有一個方法, 就是當過濾器不通過的時候執行的. 來看一下吧.

protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {actionResult.ExecuteResult(controllerContext); }

從這里可能還不能比較直觀的知道, 在做什么, 但是我貼一張圖, 應該就能比較清楚了

?

4.?PossiblySaveTempData()

internal void PossiblySaveTempData() {if (!base.ControllerContext.IsChildAction){base.TempData.Save(base.ControllerContext, this.TempDataProvider);} }

這里是保存 TempData數據

?

二、擴展或功能

1. 給方法取別名

我這里使用的是前面講路由的例子, 目錄結構如下圖:

[ActionName("Get")] public ActionResult GetA(int id ) {return View(id); }

如果沒有這個別名, 我這里肯定是找不到這個視圖的, 請求的路徑也應該是 Home/GetA的方式. 那么下面看一下結果:

從這里的結果來看, 我的請求路徑是 Home/Get 方式

?那如果我的別名和我的另一個方法相同, 那么就算我的參數并不一樣, 但是會報錯的. MVC 不知道該用哪一個方法了.

目錄已同步

轉載于:https://www.cnblogs.com/elvinle/p/6293071.html

總結

以上是生活随笔為你收集整理的MVC源码分析 - Action查找和过滤器的执行时机的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。