asp.net Mvc:filter and Action Executive Introduction _ Practical Tips

Source: Internet
Author: User
Tags reflection

A Controller object is instantiated correctly according to the name of the controller. Back to Mvchandler's BeginProcessRequest method, you can see that when you get the Controller object, you first determine whether it is iasynccontroller, and if so, create a delegate to execute asynchronously. Typically, we inherit from the controller class, which is not a iasynccontroller, and executes the controller execute method directly. The Execute method is defined in the base class controllerbase of controller, which removes some security checks and initializes controllercontext (containing information about controllerbase and request). The core is the invocation of the Executecore method, which is an abstract method in Controllerbase and is implemented in the Controller class:

Copy Code code as follows:

protected override void Executecore () {
Possiblyloadtempdata ();
try {
String actionname = routedata.getrequiredstring ("action");
if (! Actioninvoker.invokeaction (ControllerContext, actionname)) {
Handleunknownaction (ActionName);
}
}
finally {
Possiblysavetempdata ();
}}

This method is relatively simple, the first is to load temporary data, this is only when the child action will appear, not discussed. The next step is to get the name of the action, and then Invokeaction, where the Actioninvoker is a controlleractioninvoker type of object, we look at its Invokeaction method,
Copy Code code as follows:

Public virtual bool Invokeaction (ControllerContext ControllerContext, string actionname) {
if (ControllerContext = = null) {
throw new ArgumentNullException ("ControllerContext");
}
if (String.IsNullOrEmpty (ActionName)) {
throw new ArgumentException (Mvcresources.common_nullorempty, "actionname");
}
Controllerdescriptor controllerdescriptor = Getcontrollerdescriptor (ControllerContext);
Actiondescriptor actiondescriptor = findaction (ControllerContext, Controllerdescriptor, actionName);
if (actiondescriptor!= null) {
FilterInfo FilterInfo = Getfilters (ControllerContext, actiondescriptor);
try {
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);
}
else {
if (controllerContext.Controller.ValidateRequest) {
ValidateRequest (ControllerContext);
}
idictionary<string, object> parameters = Getparametervalues (ControllerContext, actiondescriptor);
ActionExecutedContext Postactioncontext = invokeactionmethodwithfilters (ControllerContext, Filterinfo.actionfilters, actiondescriptor, parameters);
Invokeactionresultwithfilters (ControllerContext, Filterinfo.resultfilters, Postactioncontext.result);
}
}
catch (ThreadAbortException) {
This type of exception occurs as a-Response.Redirect (), but we special-case so
The Filters don ' t is as an error.
Throw
}
catch (Exception ex) {
Something blew up and so execute the exception filters
Exceptioncontext Exceptioncontext = Invokeexceptionfilters (ControllerContext, Filterinfo.exceptionfilters, ex);
if (!exceptioncontext.exceptionhandled) {
Throw
}
Invokeactionresult (ControllerContext, Exceptioncontext.result);
}
return true;
}
Notify controller that no method matched
return false;}

This is a very core approach, and there is a lot of work done in this area. asp.net mvc has several types that end in descriptor, first getting controllerdescriptor, which is simpler and actually returns the Reflectedcontrollerdescriptor object. The second step is actually to call the Reflectedcontrollerdescriptor Findaction method, to obtain the Actiondescriptor,actiondescriptor most important attribute is a methodinfo, This is the method that corresponds to the action of the current action name. The Findaction method internally actually invokes the Actionmethodselector Findactionmethod to get MethodInfo, which, conceivably, will reflect the names of all the methods controller And then the action name matches, in fact, ASP. NET also supports some additional functions, mainly: 1. Rename the action by the Actionnameattribute property, 2. Support Actionmethodselectorattribute to filter the action method, such as [ HttpPost] sort of. The following simple look at the implementation of Actionmethodselector, roughly divided into 4 steps, first of all in the constructor called the following method reflection controller all the action methods:
Copy Code code as follows:

private void Populatelookuptables () {
methodinfo[] Allmethods = controllertype.getmethods (BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
methodinfo[] Actionmethods = Array.findall (Allmethods, Isvalidactionmethod);
Aliasedmethods = Array.findall (Actionmethods, Ismethoddecoratedwithaliasingattribute);
Nonaliasedmethods = Actionmethods.except (aliasedmethods). ToLookup (method => method. Name, stringcomparer.ordinalignorecase);
The}findactionmethod method is as follows:
Public MethodInfo Findactionmethod (ControllerContext controllercontext, string actionname) {
list<methodinfo> methodsmatchingname = Getmatchingaliasedmethods (ControllerContext, actionName);
Methodsmatchingname.addrange (Nonaliasedmethods[actionname]);
list<methodinfo> finalmethods = Runselectionfilters (ControllerContext, methodsmatchingname);
Switch (finalmethods.count) {
Case 0:
return null;
Case 1:
return finalmethods[0];
Default
Throw Createambiguousmatchexception (Finalmethods, actionname);
} }

This method is clear enough to find the name that fits after renaming, and then all the methods determine whether the actionmethodselectorattribute conditions are met, and then either return a matching methodinfo, throw an exception, or return NULL. The three-step implementation is not difficult and is no longer analyzed.
The third step is to get the filter. FilterInfo FilterInfo = Getfilters (ControllerContext, actiondescriptor); The actual call is:
FilterProviders.Providers.GetFilters (ControllerContext, actiondescriptor); Here the code style is not the same as before, especially like using various delegates, read code a little difficult, Probably not the same person. The following analysis directly gives the code that is actually executed. First look at the Filterprovider constructor:
Copy Code code as follows:

Static Filterproviders () {
Providers = new Filterprovidercollection ();
Providers.add (globalfilters.filters);
Providers.add (New Filterattributefilterprovider ());
Providers.add (New Controllerinstancefilterprovider ());
}

Recall the ASP.net to add filter to the action in a total of the following ways:
1. Register the global filter in Application_Start
2. Add filter to action method or controller by attribute
3. The Controller class itself also implements several interfaces such as Iactionfilter. Add filter by overriding several related methods of the Controller class.
These three methods correspond to three filterprovider, the implementation of these three provider is not very difficult, do not analyze. So far, the preparations are good, and then the filter and action,asp will be executed. NET's filter has 4 categories:


filter Type runs before and after the R Esult is executed
exception ie Xceptionfilter runs if another filter or action to throws an exception
The following is the implementation of the source code, the first is invokeauthorizationfilters:
Copy Code code as follows:

Protected virtual AuthorizationContext invokeauthorizationfilters (ControllerContext controllercontext, IList< iauthorizationfilter> filters, Actiondescriptor actiondescriptor) {
AuthorizationContext context = new AuthorizationContext (ControllerContext, actiondescriptor);
foreach (Iauthorizationfilter filter in filters) {
Filter. Onauthorization (context);
if (context. Result!= null) {
Break
}
}
return context;}

Note that in implementing the Iauthorizationfilter interface, to indicate a validation failure, you need to set the result of the parameter context to ActionResult in the Onauthorization method, indicating the page that needs to be displayed after the validation fails. Next, if the validation fails, the result of the context is executed, and if the success is performed getparametervalues gets the action parameter, the model Binding inside this method, This is also an important feature of ASP.net, another article. Then the invokeactionmethodwithfilters and Invokeactionresultwithfilters are executed separately, and the structures of the two methods are similar, Just one is to execute the action method and the Iactionfilter, one is to execute ActionResult and Iresultfilter. Take Invokeactionmethodwithfilters as an example to analyze the following:
Copy Code code as follows:

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

This code is a bit of a functional style, and people who are unfamiliar with this style look a little hard to understand. In the case of a functional programming language, the aggregate here is actually FOLDR,
FOLDR::(a->b->b)->b->[a]->b
Foldr accepts a function as the first argument, this function has two arguments, the type is a,b, the return type is B, the second argument is type B, the starting value, and the third parameter is an array of type A, and the function of FOLDR is to sequence A and the last invocation of the first parameter function (f) in the array. The return value is invoked as two parameters of F, and the first time the F is invoked with the starting value. For C #, the object-oriented representation is implemented as an extended method of ienummerable, and because C # cannot pass functions directly to the parameters of a function, the incoming delegate is. It's a mouthful to say, look at an example:
Copy Code code as follows:

static void Aggtest ()
{
int[] data = {1, 2, 3, 4};
var res = data. Aggregate ("String", (str, val) => str + val. ToString ());
Console.WriteLine (RES);
}

The result of the final output is String1234. Back to the realization of invokeactionmethodwithfilters, where the corresponding type A is iactionfilter, type B is func<actionexecutedcontext> The initial value is continuation. Assuming we have 3 filter,[f1,f2,f3], let's look at what thunk ultimately is,
First time: Next=continue, FILTER=F1, return value () =>invokeactionmethodfilter (F1, Precontext, continue)
Second time: next= () =>invokeactionmethodfilter (F1, Precontext, continue), FILTER=F2
return value: () =>invokeactionmethodfilter (F2, Precontext, () => invokeactionmethodfilter (F1, Precontext, continue))
Final: thunk= () =>invokeactionmethodfilter (F3,precontext, () =>invokeactionmethodfilter (F2, PreContext, () => Invokeactionmethodfilter (F1, Precontext, continue));
Until return thunk (), all the real code is not executed, the key is to build a thunk this delegate, the thunk to expand into the above, you should be more aware of the actual call order. It takes a lot of text to explain how to construct the call chain through the aggregate method, here is an article specifically introduced this, also can refer to. Imagine, if the function of filter is to traverse the executing method of calling F first, then call the action method, and then call the executed method of F in turn, then it can be realized by iteration, so it is not necessary to be so abstract and complex, the key is ASP.net MVC for the processing of the exception in the filter there are some special, look at the implementation of the Invokeactionmethodfilter:
Copy Code code as follows:

Internal static ActionExecutedContext Invokeactionmethodfilter (Iactionfilter filter, ActionExecutingContext Precontext, func<actionexecutedcontext> continuation) {
Filter. OnActionExecuting (Precontext);
if (Precontext.result!= null) {
return new ActionExecutedContext (Precontext, Precontext.actiondescriptor, True/* canceled/, NULL/* Exception * *) {
result = Precontext.result
};
}
BOOL Waserror = false;
ActionExecutedContext postcontext = null;
try {
Postcontext = continuation ();
}
catch (ThreadAbortException) {
This type of exception occurs as a-Response.Redirect (), but we special-case so
The Filters don ' t is as an error.
Postcontext = new ActionExecutedContext (Precontext, Precontext.actiondescriptor, False/* Canceled * *, NULL/* Exception * /);
Filter. OnActionExecuted (Postcontext);
Throw
}
catch (Exception ex) {
Waserror = true;
Postcontext = new ActionExecutedContext (Precontext, Precontext.actiondescriptor, False/* Canceled * *, ex);
Filter. OnActionExecuted (Postcontext);
if (!postcontext.exceptionhandled) {
Throw
}
}
if (!waserror) {
Filter. OnActionExecuted (Postcontext);
}
return postcontext;
}

The code is a bit long, first of all, it triggers the OnActionExecuting method of the filter, which is the core of the method. The next point is Postcontext = continuation (); Finally, the OnActionExecuted method, combined with the above expansion, we can see that the real call order will be:
Copy Code code as follows:

F3. Executing->f2. Executing->f1. Exectuing->invokeactionmethod->f1. Executed->f2->executed->f3. Executed.

So, the comments in the source code//need to reverse the filter list because the continuations are built the meaning of the up backward is also clear. The correct order of execution is required after the filter is sorted backwards.
Another type of filter is triggered when an anomaly occurs. In the Invokeaction method, you can see that the code that triggers it is placed in a catch block. The triggering process of iexceptionfilter is simpler and does not explain much more. The only thing to note is that when the Exceptionhandled property is set to true, it does not throw an exception, which is under various context, and they are the same effect. For example, you can set the OnActionExecuted method to true, as well as not throw an exception. These are simpler, and no longer analyze their source code, this article describes in more detail the sequence of execution after an exception occurs in the filter process.
Finally, the execution of action method, preceded by the MethodInfo, and the parameters obtained through the data binding, call action method should be everything. asp.net mvc side of the process is still more complex, Reflectedactiondescriptor will invoke the Actionmethoddispatcher Execute method, this method is as follows:
Copy Code code as follows:

public Object Execute (Controllerbase controller, object[] parameters) {
Return _executor (Controller, parameters);
}

The _executor here is
Delegate Object Actionexecutor (Controllerbase controller, object[] parameters); _exectuor is assigned by a method, Use expression to spell out method body, parameter, code in (ActionMethodDispatcher.cs):
Static Actionexecutor Getexecutor (MethodInfo MethodInfo) is not posted here, more complex. This makes me more puzzling is that since methodinfo and parameters have, directly with reflection on it, why so complicated, I will change the above Execute method:
Copy Code code as follows:

public Object Execute (Controllerbase controller, object[] parameters) {
Return Methodinfo.invoke (Controller, parameters);
Return _executor (Controller, parameters);
}

The results of the operation are exactly the same. I believe that the MVC source code to achieve this must have its consideration, this need to continue to study.
Finally, attach a function call diagram to understand, for reference only. Large picture, click to see the original image.


Related Article

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.