Asp.net MVC source code analysis-action Filter

Source: Internet
Author: User

Next I will go to Asp.net MVC source code analysis-controllerl controllerdescriptor. Now let's take a lookFilterinfo = getfilters (controllercontext, actiondescriptor );This sentenceCodeIt means to get the current filterinfo information, and this method is very simple. Return new filterinfo (_ Getfiltersthunk (controllercontext, actiondescriptor));

First, let's see that _ getfiltersthunk is the above thing:

Private func <controllercontext, actiondescriptor, ienumerable <filter> _ getfiltersthunk = (CC, AD) => filterproviders. providers. getfilters (CC, AD );

It means to obtain all the filter instances based on the current controllercontext and actiondescriptor. Here we mention a filterproviders. Providers stuff,

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;
}
}

Look at the three filterproviders and globalfilters provided by MVC by default. filters has nothing to say, It means to register the global Filiter processing, in our default global. asax. this sentence exists in CS application_start ().

Registerglobalfilters (Globalfilters. Filters); So globalfilters. filters is quite clear. Next let's take a look at providers. Add (New controllerinstancefilterprovider ())

public class controllerinstancefilterprovider: ifilterprovider {
Public ienumerable getfilters (controllercontext, actiondescriptor) {
If (controllercontext. controller! = NULL) {
// use filterscope. first and order of int32.minvalue to ensure controller instance methods always run first
yield return n EW filter (controllercontext. controller, filterscope. first, int32.minvalue) ;
}< BR >}

This controllerinstancefilterprovider is very special. We don't need to worry about it. The reason for this is that,This is because the Controller implements iactionfilter, iauthorizationfilter, iexceptionfilter, and iresultfilter. If the controllerinstancefilterprovider is not available, then the Controller implements these interfaces, because the corresponding methods cannot be called.

The implementation of filterattributefilterprovider is relatively complicated, but it is easy to understand. It mainly obtains the filterattribute instances of the current controller and action.ArticleThe mentioned items include concurrentdictionary <methodinfo, readonlycollection <filterattribute> _ methodfilterattributecache and concurrentdictionary <type, readonlycollection <filterattribute> _ typefilterattributecache?

Now let's take a look at the filter constructor. we need to pay attention to the following words:

If (Order = NULL ){
 Imvcfilter mvcfilter = instance as imvcfilter;
If (mvcfilter! = NULL ){
Order = mvcfilter. Order;
}
}
Order = order ?? Defaultorder;

Scope = scope;

When our filter is created through controllerinstancefilterprovider, its order is not null, so we don't need to consider it, but scope = Global

If globalfilters is used for filter. if filters is used to create an object, check whether order is added. If no order exists, check whether imvcfilter is implemented for the current instance. If yes, it is the order of the interface, if not, it is-1, and scope = first.

If it is created through filterattributefilterprovider, the order it comes in is null, but filterattribute implements the imvcfilter interface, so its order is filter by default. defaultorder (-1), while scope is controller and action. It mainly depends on whether the attribute is on controller or action.

InIn these filter sets, there is a certain order, first sort by order from small to large, if order is the same, then sort by scope from small to large, and finally remove duplicate.

Now let's take a look at the enough functions of 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> ());
}

Based on the current Filiter set, place them in different filter interface sets.

Authorizationcontext authcontext = invokeauthorizationfilters (controllercontext,Filterinfo. authorizationfilters, Actiondescriptor );
If (authcontext. Result! = NULL ){
// The auth filter signaled that we shocould let it short-circuit the request
Invokeactionresult (controllercontext, authcontext. Result );
}

ThisA few words can be used to verify the identity of the request.If it fails, an authentication failure is returned. This is important, especially when designing permissions.

Continue after successful verification

If (controllercontext. Controller. validaterequest ){
Validaterequest (controllercontext );
}

Controller. The default value of validaterequest is true, that is, the client request needs to be verified,

If (controllercontext. ischildaction) {return ;}
Validationutility. enabledynamicvalidation (httpcontext. Current );
Controllercontext. httpcontext. Request. validateinput ();

There are a lot of specific verification content, which is very troublesome. Ignore it here.

Let's sum up the following:If we want to give the entire applicationProgramTo add some processing, you can use globalfilters. filters to add special processing to a controller class or action. You can use filterattributefilterprovider to add some processing. Actionfilterattribute is often used in our actual development.

Public abstract class actionfilterattribute: filterattribute, iactionfilter, iresultfilter

Of course, if you have special requirements, you can add your own filterprovider. For example, you can write it in application_start ().Filterproviders. providers. Add (XXXXX );

Next, let's take a look at how these filters are called. Our intuitive call sequence should be

Onactionexecuting
Action... actually calls action
Onactionexecuted

Onresultexecuting
Result... actually calls result
Onresultexecuted

And the rest of the code only has this

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); this method is used to obtain the value corresponding to the action parameter. The implementation is very complicated.

When we see the invokeactionmethodwithfilters method, we know that the implementation function is to call two methods of action and iactionfilter:

Protected virtual actionexecutedcontext parameters (controllercontext, ilist <iactionfilter> filters, actiondescriptor, idictionary <string, Object> parameters) {controllercontext = new parameters (controllercontext, parameters, 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 ();}

I have read this code for a long time before I can understand its details. Actionexecutingcontext precontext = new actionexecutingcontext (controllercontext, actiondescriptor, parameters); there is nothing to say about this sentence. Simply instantiate an actionexecutingcontext object,

 

Func <actionexecutedcontext> continuation = () =>
New actionexecutedcontext (controllercontext, actiondescriptor, false/* canceled */, null/* exception */){
Result = invokeactionmethod (controllercontext, actiondescriptor, parameters)
};

There is a method for continuation that does not input a parameter to return the actionexecutedcontext instance, but this instance has a result attribute, which is the return value after the action is called (actionresult type ).

Func <actionexecutedcontext> thunk = filters. Reverse (). Aggregate (continuation,
(Next, filter) => () => invokeactionmethodfilter (filter, precontext, next ));
Return thunk ();

The key here is the usage of aggregate. continuation is an initial value. The result of each invokeactionmethodfilter (filter, precontext, next) is assigned to the continuation value. The invokeactionresultfilter code is roughly as follows:

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;
}

The method does not modify the value of continuation. When the filter loop executes the filter. when onresultexecuting (precontext) is finished, the continuation method is called. After the action is executed, the filter is cyclically executed to execute the filter. onresultexecuted (postcontext );

In short, this code is hard to understand. I personally think it is better to split and write it.

Foreach (Filiter ,...)
{
Filter. onresultexecuting (precontext );
}
Postcontext = continuation ();
Foreach (Filiter ,...)
{
Filter. onresultexecuted (postcontext );
}

I wrote a syntax test to understand the code.

Class program {static void main (string [] ARGs) {list <string> Users = new list <string> {"majiang", "luyang", "xieyichuan "}; func <string> seed = () => "first"; 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, eclipseed, func <taccumulate, tsource, taccumulate> func) {taccumulate local = seed; foreach (tsource local2 in source) {local = func (local, local2) ;}return local ;}}

the remaining invokeactionresultwithfilters methods are the same as the invokeactionmethodwithfilters method calls.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.