Asp.net MVC source code analysis -- chained call of Action Filter

Source: Internet
Author: User

 

In the previous article, we introduced the Filter types of asp.net MVC and the call time point. today, let's take a look at the call details of ActionFilter/ResultFilter and the amazing code implementation in the source code. first, we can see that IActionFilter/IResultFilter has been implemented in the Contoller class, and their interface implementation is implemented by calling two virtual functions, which provides us with convenience, we can rewrite these virtual functions in our Controller to intercept and implement our own logic.

Controller. cs

 

1 protected virtual void OnActionExecuting (ActionExecutingContext filterContext ){

2}

3

4 protected virtual void OnActionExecuted (ActionExecutedContext filterContext ){

5}

6

7 protected virtual void OnAuthorization (AuthorizationContext filterContext ){

8}

9

10 protected virtual void OnException (ExceptionContext filterContext ){

11}

12

13 protected virtual void OnResultExecuted (ResultExecutedContext filterContext ){

14}

15

16 protected virtual void OnResultExecuting (ResultExecutingContext filterContext ){

17}

18

19

20 # region IActionFilter Members

21 void IActionFilter. OnActionExecuting (ActionExecutingContext filterContext ){

22 OnActionExecuting (filterContext );

23}

24

25 void IActionFilter. OnActionExecuted (ActionExecutedContext filterContext ){

26 OnActionExecuted (filterContext );

27}

28 # endregion

29

30 # region IResultFilter Members

31 void IResultFilter. OnResultExecuting (ResultExecutingContext filterContext ){

32 OnResultExecuting (filterContext );

33}

34

35 void IResultFilter. OnResultExecuted (ResultExecutedContext filterContext ){

36 OnResultExecuted (filterContext );

37}

38 # endregion

But how can we obtain the Filter interface implemented by the Controller class? See ControllerInstanceFilterProvider. The Filter Provider that has been registered by default can get the implementation of these Filter interfaces.

ControllerInstanceFilterProvider. cs

 

1 public class ControllerInstanceFilterProvider: IFilterProvider {

2 public IEnumerable <Filter> GetFilters (ControllerContext controllerContext, ActionDescriptor actionDescriptor ){

3 if (controllerContext. Controller! = Null ){

4 // Use FilterScope. First and Order of Int32.MinValue to ensure controller instance methods always run first

5 yield return new Filter (controllerContext. Controller, FilterScope. First, Int32.MinValue );

6}

7}

8}

Bytes ---------------------------------------------------------------------------------------------

However, please consider one question, if we mark the Action Filter in the Action at the same time, we will generate a call to call two or more Action filters. How does MVC handle this situation? Please refer to the InvokeActionMethodWithFilters method in the ControllerActionInvoker. InvokeAction method. We are playing the role today.

ControllerActionInvoker. cs

 

1 protected virtual ActionExecutedContext InvokeActionMethodWithFilters (ControllerContext controllerContext, IList <IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary <string, object> parameters ){

2 ActionExecutingContext preContext = new ActionExecutingContext (controllerContext, actionDescriptor, parameters );

3 Func <ActionExecutedContext> continuation = () =>

4 new ActionExecutedContext (controllerContext, actionDescriptor, false/* canceled */, null/* exception */){

5 Result = InvokeActionMethod (controllerContext, actionDescriptor, parameters)

6 };

7

8 // need to reverse the filter list because the continuations are built up backward

9 Func <ActionExecutedContext> thunk = filters. Reverse (). Aggregate (continuation,

10 (next, filter) => () => InvokeActionMethodFilter (filter, preContext, next ));

11 return thunk ();

12}

First, we can see such a delegate in the code that declares Func <ActionExecutedContext> continuation. Note that the Declaration is not called yet. this delegate is used to call the Action method. next, I declared a delegate like Func <ActionExecutedContext> thunk. The content of this delegate is filters. the Aggregate method is used to merge our Filter execution chains. (if we do not understand them here, we need to see the description of the Aggregate method ). the InvokeActionMethodFilter method is to execute the interface implementation in IActionFilter.

InvokeActionMethodFilter Method

 

1 internal static ActionExecutedContext InvokeActionMethodFilter (IActionFilter filter, ActionExecutingContext preContext, Func <ActionExecutedContext> continuation ){

2 filter. OnActionExecuting (preContext );

3 if (preContext. Result! = Null ){

4 return new ActionExecutedContext (preContext, preContext. ActionDescriptor, true/* canceled */, null/* exception */){

5 Result = preContext. Result

6 };

7}

8

9 bool wasError = false;

10 ActionExecutedContext postContext = null;

11 try {

12 postContext = continuation ();

13}

14 catch (ThreadAbortException ){

15 // This type of exception occurs as a result of Response. Redirect (), but we special-case so that

16 // the filters don't see this as an error.

17 postContext = new ActionExecutedContext (preContext, preContext. ActionDescriptor, false/* canceled */, null/* exception */);

18 filter. OnActionExecuted (postContext );

19 throw;

20}

21 catch (Exception ex ){

22 wasError = true;

23 postContext = new ActionExecutedContext (preContext, preContext. ActionDescriptor, false/* canceled */, ex );

24 filter. OnActionExecuted (postContext );

25 if (! PostContext. ExceptionHandled ){

26 throw;

27}

28}

29 if (! WasError ){

30 filter. OnActionExecuted (postContext );

31}

32 return postContext;

33}

Bytes ---------------------------------------------------------------------------------------------

Let's try to understand the above Code. First, please refer to the call description and Demo of the Aggregate method:

 

1 public static TAccumulate Aggregate <TSource, TAccumulate> (

2 this IEnumerable <TSource> source,

3 TAccumulate seed,

4 Func <TAccumulate, TSource, TAccumulate> func

5)

6 // demo

7

8 int [] ints = {4, 8, 8, 3, 9, 0, 7, 8, 2 };

9 // Count the even numbers in the array, using a seed value of 0.

10 int numEven = ints. Aggregate (0, (total, next) =>

11 next % 2 = 0? Total + 1: total );

12

13 Console. WriteLine ("The number of even integers is: {0}", numEven );

14

15 // This code produces the following output:

16 //

17 // The number of even integers is: 6

We can see that the first parameter of Aggregate is the initial value and determines the return type of the Aggregate method. The second parameter is a delegate, and the first parameter of the delegate is the return of the last call, the returned type is also a delegate.

Bytes ---------------------------------------------------------------------------------------------

Now let's look at the thunk variable declaration in our InvokeActionMethodWithFilters method:

The first parameter is continuation. Its type is Func <ActionExecutedContext>, which determines that the return type of our Aggregate method is Func <ActionExecutedContext>, the second parameter is also a delegate (note that this method is not called here .) its implementation is to call the InvokeActionMethodFilter method. this delegate serves as the Next parameter for the third iteration (if any.

Therefore, the Next parameter in the InvokeActionMethodFilter method is the continuation variable for the first time, and the second parameter is () => InvokeActionMethodFilter (filter, preContext, next ));

. If there are three or N filters for the third or N times, the second operation will be the same, and the last result returned to the thunk variable is the last generated call delegate.

Bytes ---------------------------------------------------------------------------------------------

Sorry, I can't find any other words. I can only use the last picture to help you understand it.

Pseudocode during declaration:

Var fun1 = (next, filter) => () => InvokeActionMethodFilter (filter, preContext, continuation );

Var fun2 = (next, filter) => () => InvokeActionMethodFilter (filter, preContext, fun1)

Var fun3 = (next, filter) => () => InvokeActionMethodFilter (filter, preContext, fun2)

Eventually thunk = fun3;

When thunk () is called, the execution sequence is as follows.

Pseudocode for calling:

Invoke (next, filter) => () => InvokeActionMethodFilter (filter, preContext, fun3)

Invoke (next, filter) => () => InvokeActionMethodFilter (filter, preContext, fun2)

Invoke (next, filter) => () => InvokeActionMethodFilter (filter, preContext, continuation );

 

 

Finally, the call of ResultFilter is the same as the above analysis. If you understand this, you will understand the call of ResultFilter.

 


 

From the rain of November

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.