一、前言
之前也閱讀過MVC的源碼,並了解過各個模塊的運行原理和執行過程,但都沒有形成文章(所以也忘得特別快),總感覺分析源碼是大神的工作,而且很多人覺得平時根本不需要知道這些,會用就行了。其實閱讀源碼是個很好的習慣,它不只停留在知道怎么用的階段,而是讓我們知道一系列的為什么,為什么這樣設計,為什么這樣使用...。很多朋友應該看過《asp.net x 框架揭秘》這本書,確實不錯,特別是邊看源碼邊看書,可以有不小的收獲。Ok,我不是大神,我只是心血來潮想看一下源碼!
二、幾種常見的Filter
說到mvc里的Filter,自然會想到IAuthorizationFilter,IActionFilter,IResultFilter,IExceptionFilter,搜索一下也都知道怎么用了。其實說白了,這些接口定義了一系列方法,這些方法在請求的不同時機被執行,所謂Filter,就是讓我們可以在不同時機進行攔截處理。
這里還涉及到一個特性:FilterAttribute,例如常用的AuthorizeAttribute就繼承了FilterAttribute和實現了IAuthorizationFilter接口。說到Attribute,馬上會關聯到:運行時、反射、性能。框架會在運行過程中,通過反射獲取標記屬性,並執行特定的操作;至於性能問題,通常可以通過緩存來優化。
所以,我們可以做出猜測,以AuthorizeAttribute為例,msdn說它可以進行權限驗證,也就是在Action執行前,框架會通過反射獲取標記在Action(或Controller)上的FilterAttribute,並執行IAuthorizationFilter定義的OnAuthorization方法,在該方法內部進行權限驗證。所以如果我們要在Action執行前做某些判斷或處理,可以 1.定義一個Attribute繼承FilterAttribute,並實現IActionFilter接口(與IAuthorizationFilter不同的是,這個時候ModelBinding已經完成);2.實現IActionFilter中的方法;3.標記在Action(或Controller上)。ok,下面就通過源碼來驗證這個過程。
三、源碼分析
Action的執行是由ActionInvoker負責的,我們直接從這里出發。IActionInvoker定義了ActionInvoker要實現的方法,該接口定義如下:
public interface IActionInvoker
{
bool InvokeAction(ControllerContext controllerContext, string actionName);
}
ControllerActionInvoker 實現了該接口,顧名思義,它用於執行Controller 的 Action方法。它的 InvokeAction如下:
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null)
{
//標記1
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
try
{
//標記2
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
if (authenticationContext.Result != null)
{
InvokeActionResult(controllerContext, authenticationContext.Result);
}
else
{
IPrincipal principal = authenticationContext.Principal;
if (principal != null)
{
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
}
//標記3
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
AuthenticationChallengeContext challengeContext =
InvokeAuthenticationFiltersChallenge(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor, authorizationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
}
//標記4
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
//標記5
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
}
}
}
catch (Exception ex)
{
//標記6
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
}
return false;
}
其實這里的6個標記已經印證了我們的猜測,先獲取各種Filter,然后在各個時機執行它們。上面標記2-6都是InvokeXXXFilters就是具體的執行方法。
但是,到這里上面我們說到的FilterAttribute還沒有出現。我們先把焦點放到標記1,GetFilters 上,它獲取一個FilterInfo。GetFilters的定義如下:
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));
}
_getFiltersThunk 是一個私有變量:
private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = FilterProviders.Providers.GetFilters;
通過定義可以看出,_getFiltersThunk 會返回一個Filter 集合(這里的Filter是一個實際的類,而上面提到的是概念性的東西,或者叫過濾器更合適),Filter 對象包裝了IXXXFilter接口對象,具體是在其Instance 屬性中。這里有點繞,但不影響,簡單的說就是 GetFilters 方法會根據 FilterProviders.Providers.GetFilters 返回的一個IEnumerable<Filter>包裝一個 FilterInfo對象。
我們先看 IEnumerable<Filter> 是如何獲取的,它通過 FilterProviders.Providers.GetFilters 獲得,FilterProviders 定義如下:
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; }
}
這里可以注冊自定義的FilterProvider,FilterProvider實際是實現了IFilterProvider(定義了GetFilters方法)的類。可以看到,mvc 默認已經准備兩個FilterProvider。調用GetFilters實際會遍歷每一個FilterProvider的GetFilters方法,以內置的FilterAttributeFilterProvider 為例,它的 GetFilters方法如下:
public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
ControllerBase controller = controllerContext.Controller;
var typeFilters = GetControllerAttributes(controllerContext, actionDescriptor)
.Select(attr => new Filter(attr, FilterScope.Controller, null));
var methodFilters = GetActionAttributes(controllerContext, actionDescriptor)
.Select(attr => new Filter(attr, FilterScope.Action, null));
return typeFilters.Concat(methodFilters).ToList();
}
這里也可以看到,Filter對象包裝了具體的過濾器。其中GetControllerAttributes,實際它會調用ControllerDescriptor的 GetFilterAttribute,該方法定義如下:
public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache)
{
return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>();
}
ok,FilterAttribute 終於出現了!GetActionAttributes 也是類似的過程。
獲取到Controller和Action的FilterAttribute,並包裝成Filter集合后,就會構建一個FilterInfo對象,該對象的作用可以從其構造函數看出:
public FilterInfo(IEnumerable<Filter> filters)
{
// evaluate the 'filters' enumerable only once since the operation can be quite expensive
var cache = filters.ToList();
var overrides = cache.Where(f => f.Instance is IOverrideFilter);
FilterScope actionOverride = SelectLastScope<IActionFilter>(overrides);
FilterScope authenticationOverride = SelectLastScope<IAuthenticationFilter>(overrides);
FilterScope authorizationOverride = SelectLastScope<IAuthorizationFilter>(overrides);
FilterScope exceptionOverride = SelectLastScope<IExceptionFilter>(overrides);
FilterScope resultOverride = SelectLastScope<IResultFilter>(overrides);
_actionFilters.AddRange(SelectAvailable<IActionFilter>(cache, actionOverride));
_authenticationFilters.AddRange(SelectAvailable<IAuthenticationFilter>(cache, authenticationOverride));
_authorizationFilters.AddRange(SelectAvailable<IAuthorizationFilter>(cache, authorizationOverride));
_exceptionFilters.AddRange(SelectAvailable<IExceptionFilter>(cache, exceptionOverride));
_resultFilters.AddRange(SelectAvailable<IResultFilter>(cache, resultOverride));
}
很明顯,它將Filter 按照各自IXXXFilter接口進行分類。在InvokeAction方法內,就是根據這個分類,在各個時機進行相應的調用的。
四、總結
通過一張圖來簡單總結一下:
