緊接著上篇 asp.net mvc源碼分析-Controllerl篇 ControllerDescriptor 現在我們該看 FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);這句代碼了,意思很好明白就是擷取當前的FilterInfo資訊,而該方法非常簡單就一句return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));
首先我們來看看_getFiltersThunk是個上面東西:
private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = (cc, ad) => FilterProviders.Providers.GetFilters(cc, ad);
意思是根據當前的ControllerContext和ActionDescriptor來擷取所有的Filter執行個體,這裡提到一個 FilterProviders.Providers東東,
public static class FilterProviders {
static FilterProviders() {
Providers = new FilterProviderCollection();
Providers.Add(GlobalFilters.Filters);
Providers.Add(new FilterAttributeFilterProvider());
Providers.Add(new ControllerInstanceFilterProvider());
}
public static FilterProviderCollection Providers {
get;
private set;
}
}
看看mvc預設就提供了這3個FilterProvider,GlobalFilters.Filters沒什麼說,意思就是註冊全域的Filiter處理,在我們預設的Global.asax.cs的Application_Start()中有這個一句
RegisterGlobalFilters(GlobalFilters.Filters);所以GlobalFilters.Filters很好明白,接下來我們看看 Providers.Add(new ControllerInstanceFilterProvider())
public class ControllerInstanceFilterProvider : IFilterProvider {
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
if (controllerContext.Controller != null) {
// Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
}
}
}
這個ControllerInstanceFilterProvider 非常特殊,我們可以不用管,之所以會有這個東東,是因為Controller實現了 IActionFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,如果沒有這個ControllerInstanceFilterProvider 那麼Controller實現這些介面就沒有意義了,因為相應的方法沒法調用。
而FilterAttributeFilterProvider的實現相對比較複雜一點,但是很好理解,主要擷取當前Controller和Action的FilterAttribute執行個體,還記得上篇而文章提到的ReflectedAttributeCache裡面有ConcurrentDictionary<MethodInfo, ReadOnlyCollection<FilterAttribute>> _methodFilterAttributeCache和ConcurrentDictionary<Type, ReadOnlyCollection<FilterAttribute>> _typeFilterAttributeCache這兩個東西嗎?
現在我們來看看Filter的建構函式,裡面有這麼幾句需要我們注意
if (order == null) {
IMvcFilter mvcFilter = instance as IMvcFilter;
if (mvcFilter != null) {
order = mvcFilter.Order;
}
}
Order = order ?? DefaultOrder;
Scope = scope;
當我們的Filter是通過ControllerInstanceFilterProvider來建立的話它的order不為null,所以我們不用考慮,而Scope =Global
如果Filter是用過GlobalFilters.Filters來建立的話,那就看在添加是是否有order,沒有order的話,就看當前執行個體是否實現了IMvcFilter,實現了的話就是介面的order,沒有則是-1;而Scope =First
如果是通過FilterAttributeFilterProvider來建立的話,它進來的order是null,但是FilterAttribute是實現了IMvcFilter介面的,所以它的order是預設是Filter.DefaultOrder (-1),而scope是Controller和Action,主要看屬性是在Controller上還是在Action上。
在這些Filter集合中,是有一定的順序的,先按照order從小到大排序,如果order相同就按Scope從小到大排序,最後去掉重複的。
現在我們來看看FilterInfo的夠著函數了:
public FilterInfo(IEnumerable<Filter> filters) {
// evaluate the 'filters' enumerable only once since the operation can be quite expensive
var filterInstances = filters.Select(f => f.Instance).ToList();
_actionFilters.AddRange(filterInstances.OfType<IActionFilter>());
_authorizationFilters.AddRange(filterInstances.OfType<IAuthorizationFilter>());
_exceptionFilters.AddRange(filterInstances.OfType<IExceptionFilter>());
_resultFilters.AddRange(filterInstances.OfType<IResultFilter>());
}
根據當前的filiter集合,把它們分別放到不同filter介面集合中。
下面 AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext,filterInfo.AuthorizationFilters, actionDescriptor);
if (authContext.Result != null) {
// the auth filter signaled that we should let it short-circuit the request
InvokeActionResult(controllerContext, authContext.Result);
}
這幾句就很好明白了,對此次請求做身分識別驗證,如果沒有通過則返回一個認證失敗。這個東西很重要尤其是在許可權設計的時候。
驗證 成功後繼續執行
if (controllerContext.Controller.ValidateRequest) {
ValidateRequest(controllerContext);
}
Controller.ValidateRequest預設值為true,即是需要驗證用戶端請求的,
if (controllerContext.IsChildAction) {return;}
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
controllerContext.HttpContext.Request.ValidateInput();
具體的驗證內容很多,很麻煩,這裡就忽略它吧。
說了這麼多我們來總結一下吧:如果我們要給整個應用程式添加一些處理,可以通過GlobalFilters.Filters來添加,如果需要給某個Controller類或則是Action添加特殊處理,可以通過FilterAttributeFilterProvider來添加。在我們實際開發中往往用到的是ActionFilterAttribute
public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
當然如果有特殊的需求我們可以添加自己的FilterProvider,如在Application_Start()中可以寫成 FilterProviders.Providers.Add(xxxxx);
接下來我我們看看這些Filter事這麼調用的,憑我們的直覺調用順序應該是
OnActionExecuting
Action ......真正調用Action
OnActionExecuted
OnResultExecuting
Result....真正調用Result
OnResultExecuted
而剩下的代碼就只有這
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);幾句了,
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);這個方法是用來擷取Action參數對應的值,實現很複雜我們計劃放到後面在將。
看到InvokeActionMethodWithFilters這個方法我們就知道實現的功能是調用Action和IActionFilter的兩個方法:
protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) { ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters); Func<ActionExecutedContext> continuation = () => new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) { Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters) }; // need to reverse the filter list because the continuations are built up backward Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation, (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next)); return thunk(); }
這段代碼我看了很久才看懂它的細節。 ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);這句就沒什麼說的了,簡單一實例化一個ActionExecutingContext對象,
Func<ActionExecutedContext> continuation = () =>
new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};
這句有個方法continuation 沒有輸入參數返回ActionExecutedContext執行個體,只是這個執行個體有一個Result 屬性,它的之就是我們調用Action後的傳回值(ActionResult類型)。
Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
return thunk();
這裡關鍵是Aggregate的用法,continuation是一個初始值,將每次 InvokeActionMethodFilter(filter, preContext, next));的結果賦給continuation值,InvokeActionResultFilter的代碼大致如下:
internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
filter.OnResultExecuting(preContext);
bool wasError = false;
ResultExecutedContext postContext = null;
postContext = continuation();
filter.OnResultExecuted(postContext);
return postContext;
}
這裡個方法沒有修改continuation的值,當filter迴圈執行 filter.OnResultExecuting(preContext)完了的時候就開始調用continuation方法了,在執行了Action,再次迴圈filter 以執行 filter.OnResultExecuted(postContext);
總之這段代碼寫的很難看懂,我個人認為拆開寫要好些
foreach(Filiter ,...)
{
filter.OnResultExecuting(preContext);
}
postContext = continuation();
foreach(Filiter ,...)
{
filter.OnResultExecuted(postContext);
}
我當時為了理解這段代碼自己寫了一個文法測試
class Program { static void Main(string[] args) { List<string> users = new List<string> { "majiang", "luyang", "xieyichuan" }; Func<string> seed = () => "First"; Func<Func<string>, string, Func<string>> next = (fun, str) => delegate() { Console.WriteLine(str); var temp = fun(); temp += "-" + str; Console.WriteLine(temp); return temp; }; Func<string> result = users.Aggregate(seed, next); result(); Console.ReadLine(); } } static class Helper { public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func) { TAccumulate local = seed; foreach (TSource local2 in source) { local = func(local, local2); } return local; } }
剩下的InvokeActionResultWithFilters方法和這裡的InvokeActionMethodWithFilters方法調用一樣