Detailed description of ASP. net mvc filters, asp. netmvc
In the Action execution process of ActionInvoker, in addition to using ActionDescriptor to execute the Action method and the Model binding and verification previously carried out, it also has an important task, that is, the execution of related filters. ASP. net mvc filter is a design based on AOP (Aspect-Oriented Programming). We implement some non-business logic in the corresponding filter, and then use a cross-cutting (Crosscutting) to the corresponding Action method. These filters are automatically executed before and after the Action method is executed. ASP. net mvc provides four types of filters (AuthorizationFilter, ActionFilter, ResultFilter, and predictionfilter), which correspond to the corresponding filter interfaces (IAuthorizationFilter, IActionFilter, IResultFilter, and IExceptionFilter ). [This Article has been synchronized to How ASP. net mvc Works? Medium]
Directory
I. Filter
Ii. FilterProvider
3. FilterAttribute and FilterAttributeFilterProvider
Iv. Controller and ControllerInstanceFilterProvider
5. GlobalFilterCollection
6. Example: Verify the Filter provision mechanism and execution sequence
I. Filter
Although the four types of filters provided by ASP. net mvc have their own Implemented interfaces, all filters are represented by the Filter type defined below in the Filter provision system. The core of Filter is the Instance attribute, because it represents the objects that actually implement the filtering function. This object implements one or more interfaces based on the above four Filter types.
public class Filter { public const int DefaultOrder = -1; public Filter(object instance, FilterScope scope, int? order); public object Instance { get; protected set; } public int Order { get; protected set; } public FilterScope Scope { get; protected set; } } public enum FilterScope { Action = 30, Controller = 20, First = 0, Global = 10, Last = 100 }
Note: Because System. web. mvc. the Filter and IAuthorizationFilter, IActionFilter, IResultFilter, and IExceptionFilter types can all be called "filters". To avoid confusion, without explicit descriptions, we use English "Filter" and Chinese "Filter" to represent them respectively.
The Order and Scope attributes of the Filter determine the execution sequence of the Filter. The smaller the value corresponding to the Order attribute, the higher the execution priority. The default value of this attribute is-1 (corresponding to the constant DefaultOrder defined in the Filter ). If the two filters have the same Order attribute value, the Scope attribute determines which Filter is executed first. The Scope attribute type of Filter is an enumeration of FilterScope type. This enumeration indicates the range of filters applied. Action and Controller represent the Action method and Controller class level. First and Last indicate that they are to be executed as the First and Last filters; global represents a Global Filter.
Through the code snippet above, we can see that all the five enumeration options of FilterScope are set with a value, which determines the execution sequence of the Filter, and smaller enumeration values will be preferentially executed. From the definition of FilterScope, we can conclude that for multiple filters with the same Order attribute value, the Filter applied to the Controller has a higher execution priority than the Filter applied to the Action method, the execution priority of a global Filter is higher than that of an Action-based Filter.
Ii. FilterProvider
The Filter provision mechanism is similar to the provision mechanism based on ModelBinder and ModelValidator introduced earlier, and is provided through the corresponding Provider. FilterProvider that provides filters implements the IFilterProvider interface, as shown in the following code snippet, this interface defines the unique method GetFilters to obtain a set of Filter Objects Based on the specified Controller context and the ActionDescriptor object used to describe the target Action.
public interface IFilterProvider { IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }
We can use static FilterProviders to register or obtain the FilterProvider used by the current application. As shown in the following code snippet, FilterProviders has a read-only property Providers of the FilterProviderCollection type, which indicates the list of FilterProviders used throughout the Web application. FilterProviderCollection is a collection of IFilterProvider element types. The GetFilters method is used to Filter objects provided by all FilterProvider objects in the collection.
Public static class FilterProviders {public static FilterProviderCollection Providers {get ;}} public class FilterProviderCollection: Collection <IFilterProvider >{// other members public IEnumerable <Filter> GetFilters (ControllerContext controllerContext, actionDescriptor actionDescriptor );}
ASP. net mvc provides three native filterproviders: FilterAttributeFilterProvider, ControllerInstanceFilterProvider, and GlobalFilterCollection. Next we will introduce them separately.
3. FilterAttribute and FilterAttributeFilterProvider
We usually define a filter as a feature and apply it to the Controller type or Action method as a declaration. The abstract type FilterAttribute is the base class of all filters. As shown in the following code snippet, The FilterAttribute feature implements the IMvcFilter interface, which defines two read-only attributes: Order and AllowMultiple, these filters are used to control the execution sequence of filters and multiple filters of the same type can be applied to the same target element (class or method) at the same time ).
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)] public abstract class FilterAttribute : Attribute, IMvcFilter { protected FilterAttribute(); public bool AllowMultiple { get; } public int Order { get; set; } } public interface IMvcFilter { bool AllowMultiple { get; } int Order { get; } }
From the AttributeUsageAttribute definition of the application on FilterAttribute, we can see that this feature can be applied to the type and method, which means that the filter can generally be applied to the Controller type and Action method. The read-only attribute AllowMultiple actually returns the attribute of the same name as AttributeUsageAttribute. The above definition shows that the attribute value is False by default.
The ControllerDescriptor and ActionDescriptor used to describe the Controller and Action implement the ICustomAttributeProvider interface. We can directly use them to obtain all the features of the application, including FilterAttribute, on the corresponding Controller type or Action method. In fact, the two description types provide a separate method GetFilterAttributes for obtaining the FilterAttribute feature list. As shown in the following code snippet, this method has a Boolean parameter useCache, indicating whether to cache the parsed FilterAttribute to mitigate the impact of frequent reflection operations on performance.
Public abstract class ControllerDescriptor: ICustomAttributeProvider, other {// other members public virtual IEnumerable <FilterAttribute> GetFilterAttributes (bool useCache);} public abstract class ActionDescriptor: ICustomAttributeProvider, IUniquelyIdentifiable {// other members public virtual IEnumerable <FilterAttribute> GetFilterAttributes (bool useCache );}
Filters for the FilterAttribute feature are provided through the FilterAttributeFilterProvider object. FilterAttributeFilterProvider directly calls the GetFilterAttributes method of the current ControllerDescriptor and ActionDescriptor to obtain the FilterAttribute features of all applications in the Controller type and current Action method, and then creates the corresponding Filter object. The cacheAttributeInstances parameter of the filterfilbutefilterprovider constructor indicates whether to enable the cache for FilterAttribute. It is used as a parameter to call the GetFilterAttributes method. By default, the FilterAttributeFilterProvider created by calling the constructor without any parameters will use the FilterAttribute cache.
public class FilterAttributeFilterProvider : IFilterProvider { public FilterAttributeFilterProvider(); public FilterAttributeFilterProvider(bool cacheAttributeInstances); protected virtual IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor); protected virtual IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor); public virtual IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }
For the Filter obtained by calling GetFilters, the corresponding FilterAttribute attribute is used as its Instance attribute. The Order attribute is from the same name attribute of FilterAttribute, while the Scope attribute depends on whether the FilterAttribute is applied to the Controller type (the Scope attribute value is Controller) or the current Action method (the Scope attribute value is Action ).
Iv. Controller and ControllerInstanceFilterProvider
When it comes to ASP. net mvc filters, most of them only think of using the FilterAttribute feature. In fact, the Controller itself (inherited from the abstract class Controller) is a filter. As shown in the following code snippet, the abstract class Controller implements four interfaces corresponding to different filter types: IActionFilter, IAuthorizationFilter, IExceptionFilter, and IResultFilter.
Public abstract class Controller: ControllerBase, IActionFilter, IAuthorizationFilter, IExceptionFilter, IResultFilter,... {// omit member}
The FilterProvider type for the unique filter such as Controller object is ControllerInstanceFilterProvider with the following definitions. In the GetFilters method, it obtains the corresponding Controller object based on the specified Controller context and creates a Filter (the Controller object acts as the Instance attribute value of the Filter object ). The Scope of the Filter is not Controller, but First, and the value of Order is-2147483648 (Int32.MinValue). There is no doubt that such a Filter will be executed for the First time.
public class ControllerInstanceFilterProvider : IFilterProvider { public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); }
5. GlobalFilterCollection
Filters defined in the form of FilterAttribute must be explicitly labeled to the Target Controller type or Action method. In some cases, a global Filter is required. The global filter does not need to explicitly match a Controller or Action. Instead, it is used to execute all actions by default. The FilterProvider used to provide the global Filter corresponds to a GlobalFilterCollection with the following definitions.
public sealed class GlobalFilterCollection : IEnumerable<Filter>, IEnumerable, IFilterProvider { public GlobalFilterCollection(); public void Add(object filter); public void Add(object filter, int order); private void AddInternal(object filter, int? order); public void Clear(); public bool Contains(object filter); public IEnumerator<Filter> GetEnumerator(); public void Remove(object filter); IEnumerator IEnumerable.GetEnumerator(); IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); public int Count { get; } }
By naming and the definitions given above, we can see that GlobalFilterCollection is a list of filters, and the GetFilters method of implementation returns itself. Using the methods provided by GlobalFilterCollection, we can add, delete, and clear global filters. The Filter parameter used to Add a filter is not a Filter object, but a specific Filter (implementing the corresponding Filter interface). The added filter object is created based on the Filter object, its Scope attribute is set to Global. We specify the Order attribute of the Filter object in the Add method. If the specified Order is not displayed and the specified Filter is a FilterAttribute feature, the Order of this feature will be used as the Order of the Filter object; otherwise, use-1 as the Order attribute value.
The registration and acquisition of global Filter (or global FilterProvider) for the entire Web application can be achieved through the static type GlobalFilters. As shown in the following code snippet, GlobalFilters has a static read-only attribute. Filters returns a GlobalFilterCollection object.
public static class GlobalFilters { public static GlobalFilterCollection Filters { get; } }
So far, we have introduced three filterproviders by default provided by ASP. net mvc and their respective filters. When the static types used to register FilterProvider are loaded, these three types of objects are created by default and used as the Providers attribute value indicating the global FilterProvider set, the specific logic is embodied in the following code snippets. That is to say, ASP. net mvc uses these three filterproviders by default to provide all the Filter objects.
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;} }
6. Example: Verify the Filter provision mechanism and execution sequence
To enable readers to have a deeper image of the Filter provision mechanism described above, we will provide a simple example. In an empty Web project created using the ASP. net mvc Project template of Visual Studio, we define the following FilterAttribute. FilterBaseAttribute is an abstract type that implements the IActionFilter interface. Three specific filterattributes (FooAttribute, BarAttribute, and BazAttribute) are its successors.
public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter { public void OnActionExecuted(ActionExecutedContext filterContext) {} public void OnActionExecuting(ActionExecutingContext filterContext) {} } public class FooAttribute : FilterBaseAttribute {} public class BarAttribute : FilterBaseAttribute {} public class BazAttribute : FilterBaseAttribute {}
We first register BazAttribute as a Global filter in Global. asax as follows. It should be noted that the Application_Start method defined in the created Global. asax by default will call the RegisterGlobalFilters method to register a ExceptionFilter of the type HandleErrorAttribute. We need to annotate this line of code.
Public class MvcApplication: System. web. httpApplication {// other member protected void Application_Start () {// other operations // RegisterGlobalFilters (GlobalFilters. filters); GlobalFilters. filters. add (new BazAttribute ());}}
Finally, we created the next default HomeController. The BarAttribute feature we defined is applied to an empty Action method Data, while the FooAttribute feature is applied to the HomeController class. In the default Action method Index, we obtain all the Filter objects for Action method Data through the Global FilterProvider list represented by the static attribute Providers of FilterProviders, and present their basic information (type, Order, and Scope attributes.
[Foo] public class HomeController : Controller { public void Index() { ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController)); ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "Data"); foreach (var filter in FilterProviders.Providers.GetFilters(ControllerContext, actionDescriptor)) { Response.Write(string.Format("{0}<br/>",filter.Instance)); Response.Write(string.Format(" {0}: {1}<br/>", "Order",filter.Order)); Response.Write(string.Format(" {0}: {1}<br/><br/>", "Scope",filter.Scope)); } } [Bar] public void Data() { } }
After running our program, the result 7-5 is displayed in the browser. We can clearly see that not only the FilterAttribute applied to the Action method will be applied to the target Action, the FilterAttribute applied to the Controller class, the globally registered Filter, and the Filter embodied in the Controller object are all applied to all the actions.
The Order and Scope attributes of the four filters applied to the Action method Data are displayed. As we mentioned earlier, these two attributes determine the sequence in which filters of the same type are executed. We need to use this program to confirm this. Therefore, we need to make the following changes to FilterBaseAttribute. In OnActionExecuting, we will present the method name of the currently executed FilterAttribute type.
public abstract class FilterBaseAttribute:FilterAttribute, IActionFilter { public void OnActionExecuted(ActionExecutedContext filterContext) {} public void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Response.Write(string.Format("{0}.OnActionExecuting()<br/>", this.GetType())); } }
Then, the OnActionExecuting method of HomeController is rewritten in the same way to present the current method name of HomeController's own type.
[Foo] public class HomeController: Controller {// other member protected override void OnActionExecuting (ActionExecutingContext filterContext) {Response. write ("HomeController. onActionExecuting () <br/> ");} [Bar] public void Data (){}}
Run our program again and specify the correct address in the browser to access the Action Method Data defined in HomeController. The result shown in is displayed in the browser. The output result shows the execution sequence of the four actionfilters applied to the Action method Data, which is consistent with the Order and Scope attribute values corresponding to the Filter.
Another question worth further research is the provision of Filter: when defining FilterAttribute, we can set the AllowMultiple attribute of AttributeUsageAttribute applied on this type to False so that it can be applied only once on the same target element. However, we can still apply the Action method and Controller type, or even register them as global filters. Will these filterattributes be valid?
We will verify this through the instance now. Now we delete all FilterAttribute and define it as the next ActionFilter of the FooAttribute type. We will set the AllowMultiple attribute applied to the AttributeUsageAttribute attribute on it to False.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class FooAttribute : FilterAttribute, IActionFilter { public void OnActionExecuted(ActionExecutedContext filterContext) { } public void OnActionExecuting(ActionExecutingContext filterContext) { } }
Now we apply this FooAttribute feature to both HomeController type and Action method Data, and then register a Global Filter for the FooAttribute feature in Global. asax.
[Foo] public class HomeController: Controller {// other member [Foo] public void Data () {}} public class MvcApplication: System. web. httpApplication {// other member protected void Application_Start () {// other operations // RegisterGlobalFilters (GlobalFilters. filters); GlobalFilters. filters. add (new FooAttribute ());}}
Now we can run our program directly, and the result shown in 7-7 will be displayed in the opened browser. We can clearly see that although we have registered FooAttribute in three places, the AllowMultiple attribute of this feature is False, so only one of them is valid.
For FilterAttribute whose AllowMultiple attribute is False, which of the following statements is valid if multiple attributes are registered with different scopes? It can be seen that the FooAttribute applied to the Action method (Scope is Action) is valid. In fact, the specific logic is as follows: All the created filters are sorted by Order + Scope (that is, the Order in which the filters are executed) and ranked at the last one. In our example, the three filters have the same Order attribute value (-1), and all filters are sorted by Scope (Scope, Controller, and Action, the last one is the Filter in which Scope is Action.
The above is all the content of this article. I hope the content of this article will help you in your study or work. If you have any questions, you can leave a message and share it with us!