Use the source code to understand the execution process of several filters in ASP. net mvc, mvcfilter
I. Preface
I have also read the source code of MVC and learned about the running principle and execution process of each module, but I have not written any article (so I forget it very quickly ), I always feel that source code analysis is a great job, and many people feel that they do not need to know this at ordinary times, and they will use it. In fact, reading the source code is a good habit. It not only stays at the stage of knowing how to use it, but also shows us why, why, and why .... Many of my friends should have read the book "asp.net x framework secrets", which is really good, especially reading the source code while reading the book. OK, I am not a great god. I just want to see the source code!
2. Several Common Filters
When it comes to filters in mvc, you will naturally think of IAuthorizationFilter, IActionFilter, IResultFilter, and IExceptionFilter. You will also know how to use them when searching. To put it bluntly, these interfaces define a series of methods that are executed at different times of the request. The so-called Filter means that we can intercept and process them at different times.
A feature is also involved:FilterAttributeFor example, the commonly used AuthorizeAttribute inherits the FilterAttribute and implements the IAuthorizationFilter interface. When it comes to Attribute, it will be immediately associated with runtime, reflection, and performance. During running, the Framework obtains tag attributes through reflection and performs specific operations. For performance problems, it can be optimized through caching.
So we canMake guessesTaking AuthorizeAttribute as an example, msdn says it can perform permission verification, that is, before the Action is executed, the Framework obtains the FilterAttribute marked on the Action (or Controller) through reflection, run the OnAuthorization method defined by IAuthorizationFilter to verify the permissions of the method. So if we want to make some judgment or processing before the Action is executed, we can. define an Attribute to inherit FilterAttribute and implement the IActionFilter interface (unlike IAuthorizationFilter, ModelBinding is completed at this time); 2. implement the method in IActionFilter; 3. mark it on Action (or Controller ). OK. The following code is used to verify the process.
Iii. Source Code Analysis
Action execution is the responsibility of ActionInvoker. Let's proceed from here. IActionInvoker defines the methods to be implemented by ActionInvoker. The interface is defined as follows:
public interface IActionInvoker { bool InvokeAction(ControllerContext controllerContext, string actionName); }
ControllerActionInvoker implements this interface. As the name suggests, it is used to execute the Controller Action method. Its InvokeAction is as follows:
Public virtual bool InvokeAction (ControllerContext controllerContext, string actionName) {ControllerDescriptor descriptor = role (controllerContext); ActionDescriptor actionDescriptor = FindAction (controllerContext, role, actionName); if (actionDescriptor! = Null) {// mark 1 FilterInfo filterInfo = GetFilters (controllerContext, actionDescriptor); try {// mark 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;} // mark 3 AuthorizationContext authorizationContext = InvokeAuthorizationFilters (controllerContext, filterInfo. authorizationFilters, actionDescriptor); if (authorizationContext. result! = Null) {AuthenticationChallengeContext challengeContext = extract (controllerContext, filterInfo. AuthenticationFilters, actionDescriptor, authorizationContext. Result); InvokeActionResult (controllerContext, challengeContext. Result ?? AuthorizationContext. result);} else {if (controllerContext. controller. parameters) {ValidateRequest (controllerContext);} // mark 4 IDictionary <string, object> parameters = GetParameterValues (controllerContext, actionDescriptor); ActionExecutedContext postActionContext = callback (controllerContext, filterInfo. actionFilters, actionDescriptor, parameters); // mark 5 InvokeActionR EsultWithFilters (controllerContext, filterInfo. resultFilters, postActionContext. result) ;}} catch (Exception ex) {// mark 6 ExceptionContext exceptionContext = InvokeExceptionFilters (controllerContext, filterInfo. predictionfilters, ex); if (! ExceptionContext. ExceptionHandled) {throw;} InvokeActionResult (controllerContext, exceptionContext. Result);} return true;} return false ;}
In fact, the six tags here have proved our speculation. We first obtain various filters and then execute them at various times. InvokeXXXFilters is the specific execution method.
However, the FilterAttribute we mentioned above has not yet appeared. We first put the focus on Mark 1 and GetFilters, and it gets a FilterInfo. GetFilters is defined as follows:
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { return new FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor)); }
_ GetFiltersThunk is a private variable:
private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = FilterProviders.Providers.GetFilters;
From the definition, we can see that _ getFiltersThunk will return a Filter set (the Filter here is an actual class, and the above mentioned is conceptual, or the Filter is more appropriate ), the Filter object encapsulates the IXXXFilter interface object in its Instance attribute. The GetFilters method encapsulates a FilterInfo object according to an IEnumerable <Filter> returned by FilterProviders. Providers. GetFilters.
Let's first look at how IEnumerable <Filter> is obtained through FilterProviders. Providers. GetFilters. FilterProviders is defined as follows:
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; } }
Here you can register a custom FilterProvider. FilterProvider is actually a class that implements IFilterProvider (defines the GetFilters method. As you can see, two filterproviders are prepared by mvc by default. Calling GetFilters actually traverses the GetFilters method of each FilterProvider. Taking the built-in FilterAttributeFilterProvider as an example, its GetFilters method is as follows:
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(); }
Here we can also see that the Filter object encapsulates the specific Filter. The GetControllerAttributes actually calls the GetFilterAttribute of ControllerDescriptor. The method is defined as follows:
public virtual IEnumerable<FilterAttribute> GetFilterAttributes(bool useCache) { return GetCustomAttributes(typeof(FilterAttribute), inherit: true).Cast<FilterAttribute>(); }
OK. The FilterAttribute is displayed! GetActionAttributes is a similar process.
After obtaining the FilterAttribute of the Controller and Action and packaging it into a Filter set, a FilterInfo object is constructed. The role of this object can be seen from its constructor:
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)); }
Obviously, it classifies filters according to their IXXXFilter interfaces. In the InvokeAction method, corresponding calls are made at various times based on this classification.
Iv. Summary
The following figure shows a brief summary: