MVC源码分析 - Action查找和过滤器的执行时机
接著上一篇, 在創(chuàng)建好Controller之后, 有一個(gè) this.ExecuteCore()方法, 這部分是執(zhí)行的. 那么里面具體做了些什么呢?
//ControllerBaseprotected 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();
//在這里創(chuàng)建了控制器上下文, ControllerContextthis.Initialize(requestContext);using (ScopeStorage.CreateTransientScope()){
//加載 TempData, 創(chuàng)建及執(zhí)行 Action, 處理 Action 返回的 ActionResult, 保存TempDatathis.ExecuteCore();} }
來看一下這里的 ExecuteCore具體是執(zhí)行的那里的方法.
//System.Web.MVC.Controller protected override void ExecuteCore() {//從Session 中加載 TempData 數(shù)據(jù)this.PossiblyLoadTempData();try{
//從路由中獲取 Action 名稱string requiredString = this.RouteData.GetRequiredString("action");
//判斷部分會(huì)去執(zhí)行 Action, 并處理 Action 返回的ActionResultif (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString)){this.HandleUnknownAction(requiredString);}}finally{
//保存 TempData 數(shù)據(jù)this.PossiblySaveTempData();} }
這個(gè)類應(yīng)該還是蠻熟悉的吧, 我們創(chuàng)建的控制器類, 都會(huì)直接或者間接繼承這個(gè)類.
?
一、解析
1. PossiblyLoadTempData()
首先來看一下這個(gè)方法吧. 看看里面做了些什么
internal void PossiblyLoadTempData() {if (!base.ControllerContext.IsChildAction){base.TempData.Load(base.ControllerContext, this.TempDataProvider);} }看到這里的TempData, 感覺好熟悉吧. 我還是嘮叨一句吧.
TempData是保存在session中的, 可以用于不同Controller,不同Action,Action到View之間的傳值.
額, 要不要繼續(xù)解析一下呢, 算了, 看一下吧, 起碼看到這個(gè)數(shù)據(jù)是保存在session中的.
//TempDataDictionarypublic 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(); }
繼續(xù)看LoadTempData()方法, 真相就能大白了.
//System.Web.Mvc.SessionStateTempDataProviderpublic 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); }
從這里能看到, 我所言非虛了, 確實(shí)是存放在Session中的.
注意 : 同一個(gè) TempData 只能被傳遞一次, 當(dāng)在Session中找到TempData后, 就會(huì)將它清除掉, 下一次請(qǐng)求是不能再獲取到這個(gè)數(shù)據(jù). 因?yàn)樵赟ession中已經(jīng)被清除了.
?
2.?GetRequiredString("action")
這個(gè)方法應(yīng)該不需要多說了, 跟前面獲取控制器名稱的方式一樣, 只不過這里是用來獲取 Action 方法的名稱
?
3.?InvokeAction 這個(gè)是重點(diǎn)方法
前面既然已經(jīng)獲取到 Action 方法的名字, 那么現(xiàn)在是不是應(yīng)該去找到這個(gè)方法, 并看看是否能匹配的上呢? 匹配的時(shí)候, 用的是哪一些條件? 如果匹配的上, 又怎么執(zhí)行的呢? 謎底即將揭曉.
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");}//根據(jù)控制器上下文, 來獲取控制器描述對(duì)象ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
//這里調(diào)用的FindAction方法, 其實(shí)就是調(diào)用控制器描述類里面的 FindAction 方法, 但是這是一個(gè)抽象方法
//這里返回的是 Action 方法的描述信息
//默認(rèn)調(diào)用的是 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 過濾器, 執(zhí)行之后,會(huì)將結(jié)果存放在 ActionResult 類型的Result屬性中,
//如果返回結(jié)果不為空, 則不會(huì)再去執(zhí)行Action里面的方法和View里面的內(nèi)容了.AuthorizationContext context = this.InvokeAuthorizationFilters(controllerContext,
filters.AuthorizationFilters, actionDescriptor);if (context.Result != null){this.InvokeActionResult(controllerContext, context.Result);}else{if (controllerContext.Controller.ValidateRequest){ValidateRequest(controllerContext);}
//獲取參數(shù)的信息, 存入字典中, 以供做參數(shù)匹配IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
//Action 過濾器, Action方法的執(zhí)行也在這里面進(jìn)行ActionExecutedContext context2 = this.InvokeActionMethodWithFilters(controllerContext,
filters.ActionFilters, actionDescriptor, parameterValues);
//Result 過濾器this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);}}catch (ThreadAbortException){throw;}catch (Exception exception){
//錯(cuò)誤信息過濾器, HandleErrorExceptionContext context3 = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);if (!context3.ExceptionHandled){throw;}this.InvokeActionResult(controllerContext, context3.Result);}return true; }
乍一看, 里面的內(nèi)容真心多啊. 還是那句話, 不要怕, 一個(gè)一個(gè)來.
3.1 FindAction - 先看一下這個(gè)方法, 是怎么找到 Action 的
//ReflectedControllerDescriptorpublic 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方法的名稱去獲取到的.
那么進(jìn)去再看一下吧.
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
這里可能有點(diǎn)讓人費(fèi)解, 可能是不明白 Aliased 的意思, 翻譯過來其實(shí)是別名的意思.
這里牽涉到Action的一個(gè)功能, 就是給Action取別名. 通過 ActionNameAttribute 特性來進(jìn)行.
這里的意思, 就是獲取到所有的方法, 包括有別名的和沒有別名的. 對(duì)于聲明了 NonAliasedAttribute特性的方法, 也會(huì)獲取出來.
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; }
這里其實(shí)是對(duì)方法進(jìn)行一個(gè)過濾和一個(gè)返回優(yōu)先級(jí)的問題.
Action上的Attribute如果是ActionMethodSelectorAttribute類型或者是繼承了它的, 并且該特性的 IsValidForRequest()返回的結(jié)果是true,那么就會(huì)通過篩選, 優(yōu)先返回.
這里其實(shí)主要是為了過濾 AcceptVerbsAttributes 和 NonActionAttribute 的.
NonActionAttribute : 他的IsValidForRequest()返回的是false, 所以Action上有此特性的方法會(huì)被篩選掉.
AcceptVerbsAttributes ?: 同名方法, 必須聲明不同的此特性, post, get, 否則也還是會(huì)報(bào)錯(cuò).
3.2 GetFilters() - 獲取Controller 和 Action 中, 聲明的所有的 過濾器
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {return new FilterInfo(this._getFiltersThunk(controllerContext, actionDescriptor)); }這里的_getFiltersThunk是一個(gè)Func<>()委托, 那么具體是什么方法呢?
其實(shí)這里指向的是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.CombinedItemsselect fp.GetFilters(controllerContext, actionDescriptor))
.OrderBy<Filter, Filter>(filter => filter, _filterComparer);return this.RemoveDuplicates(source.Reverse<Filter>()).Reverse<Filter>(); }
看一下這里的GetFilters(), 他其實(shí)是IFilterProvider接口中的方法, 那么哪一些類實(shí)現(xiàn)了這個(gè)接口呢?
解析到這里, 大致已經(jīng)能知道, 獲取了哪一些過濾器了.
在更進(jìn)一步, 看一下 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過濾器, 控制器的部分下一篇會(huì)詳細(xì)描述
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext,IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor) {AuthorizationContext filterContext = new AuthorizationContext(controllerContext, actionDescriptor);foreach (IAuthorizationFilter filter in filters){
//遍歷執(zhí)行控制器方法filter.OnAuthorization(filterContext);if (filterContext.Result != null){return filterContext;}}return filterContext; }
3.4?InvokeActionMethodWithFilters() - Action 過濾器, 這里面其實(shí)會(huì)執(zhí)行兩個(gè)過濾器 : OnActionExecuting / OnActionExecuted
我們其實(shí)都知道, 這兩個(gè)過濾器中間, 還少了一個(gè)東西, 就是 Action 方法的執(zhí)行, 那么在執(zhí)行這個(gè)方法的過程之中, 就會(huì)去執(zhí)行 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))(); }
| 方法 | 參數(shù) | 描述 |
| OnActionExecuting | ActionExecutingContext | 在行為方法執(zhí)行前執(zhí)行 |
| OnActionExecuted | ActionExecutedContext | 在行為方法執(zhí)行后執(zhí)行 |
| OnResultExecuting | ResultExecutingContext | 在行為方法返回前執(zhí)行 |
| OnResultExecuted | ResultExecutedContext | 在行為方法返回后執(zhí)行 ? |
過濾器這部分內(nèi)容會(huì)在后面的篇章做出詳細(xì)解釋.
3.5?InvokeActionResultWithFilters() - Result 過濾器, 這里面也會(huì)執(zhí)行兩個(gè)過濾器 : OnResultExecuting / OnResultExecuted
這里同上面是一樣的, 這兩個(gè)過濾器中間, 少了一個(gè) View 的執(zhí)行, 也就是說, 在執(zhí)行這個(gè)方法的時(shí)候, 會(huì)動(dòng)態(tài)執(zhí)行 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()
注意到這里還有一個(gè)方法, 就是當(dāng)過濾器不通過的時(shí)候執(zhí)行的. 來看一下吧.
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {actionResult.ExecuteResult(controllerContext); }從這里可能還不能比較直觀的知道, 在做什么, 但是我貼一張圖, 應(yīng)該就能比較清楚了
?
4.?PossiblySaveTempData()
internal void PossiblySaveTempData() {if (!base.ControllerContext.IsChildAction){base.TempData.Save(base.ControllerContext, this.TempDataProvider);} }這里是保存 TempData數(shù)據(jù)
?
二、擴(kuò)展或功能
1. 給方法取別名
我這里使用的是前面講路由的例子, 目錄結(jié)構(gòu)如下圖:
[ActionName("Get")] public ActionResult GetA(int id ) {return View(id); }如果沒有這個(gè)別名, 我這里肯定是找不到這個(gè)視圖的, 請(qǐng)求的路徑也應(yīng)該是 Home/GetA的方式. 那么下面看一下結(jié)果:
從這里的結(jié)果來看, 我的請(qǐng)求路徑是 Home/Get 方式
?那如果我的別名和我的另一個(gè)方法相同, 那么就算我的參數(shù)并不一樣, 但是會(huì)報(bào)錯(cuò)的. MVC 不知道該用哪一個(gè)方法了.
目錄已同步
轉(zhuǎn)載于:https://www.cnblogs.com/elvinle/p/6293071.html
總結(jié)
以上是生活随笔為你收集整理的MVC源码分析 - Action查找和过滤器的执行时机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: placeholder这个属性 inpu
- 下一篇: 【NWERC 2015-G】Guessi