private static void Rethrow(ResultExecutedContextEx context) { if (context == null) { return; } if (context.ExceptionHandled) { return; } if (context.ExceptionDispatchInfo != null) { context.ExceptionDispatchInfo.Throw(); } if (context.Exception != null) { throw context.Exception; } }
private async Task InvokeNextResultFilterAsync() { try { var next = State.ResultNext; var state = (object)null; var scope = Scope.Result; var isCompleted = false; while (!isCompleted) { await Next(ref next, ref scope, ref state, ref isCompleted); } } catch (Exception exception) { _resultExecutedContext = new ResultExecutedContextEx(_controllerContext, _result, canceled: false, exception: exception) { ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception), }; } Debug.Assert(_resultExecutedContext != null); }
private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) { switch (next) { case State.InvokeBegin: { goto case State.AuthenticationBegin; } case State.AuthenticationBegin: { _cursor.Reset(); _authenticationContext = InvokeAuthenticationFilters(_controllerContext, _cursor.AuthenticationFilters, _actionDescriptor); if (_authenticationContext.Result != null) { _result = _authenticationContext.Result; goto case State.AuthenticationShortCircuit; } goto case State.AuthenticationEnd; } case State.AuthenticationShortCircuit: { Debug.Assert(_authenticationContext != null); Debug.Assert(_authenticationContext.Result != null); _result = _authenticationContext.Result; authenticationChallengeBypassResultFilters = true; goto case State.AuthenticationChallengeBegin; } case State.AuthenticationEnd: { goto case State.AuthorizationBegin; } case State.AuthenticationChallengeBegin: { _cursor.Reset(); _authenticationChallengeContext = InvokeAuthenticationFiltersChallenge(_controllerContext, _cursor.AuthenticationFilters, _actionDescriptor, _result); // unlike other filter types, don't short-circuit evaluation when context.Result != null (since it // starts out that way, and multiple filters may add challenges to the result) _result = _authenticationChallengeContext.Result ?? _result; goto case State.AuthenticationChallengeEnd; } case State.AuthenticationChallengeEnd: { Debug.Assert(_result != null); if (authenticationChallengeBypassResultFilters) { // short-circuit before we started invoking the action itself // so bypass any result filters isCompleted = true; InvokeActionResult(_controllerContext, _result); resultExecuted = true; goto case State.InvokeEnd; } goto case State.ResultBegin; } case State.AuthorizationBegin: { _cursor.Reset(); goto case State.AuthorizationNext; } case State.AuthorizationNext: { var current = _cursor.GetNextFilter <IAuthorizationFilter, IAsyncAuthorizationFilter>(); if (current.FilterAsync != null) { if (_authorizationContext == null) { _authorizationContext = new AuthorizationContext(_controllerContext, _actionDescriptor); } state = current.FilterAsync; goto case State.AuthorizationAsyncBegin; } else if (current.Filter != null) { if (_authorizationContext == null) { _authorizationContext = new AuthorizationContext(_controllerContext, _actionDescriptor); } state = current.Filter; goto case State.AuthorizationSync; } else { goto case State.AuthorizationEnd; } } case State.AuthorizationAsyncBegin: { Debug.Assert(state != null); Debug.Assert(_authorizationContext != null); var filter = (IAsyncAuthorizationFilter)state; var authorizationContext = _authorizationContext; var task = filter.OnAuthorizationAsync(authorizationContext); if (task.Status != TaskStatus.RanToCompletion) { next = State.AuthorizationAsyncEnd; return(task); } goto case State.AuthorizationAsyncEnd; } case State.AuthorizationAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_authorizationContext != null); var filter = (IAsyncAuthorizationFilter)state; var authorizationContext = _authorizationContext; if (authorizationContext.Result != null) { goto case State.AuthorizationShortCircuit; } goto case State.AuthorizationNext; } case State.AuthorizationSync: { var filter = (IAuthorizationFilter)state; var authorizationContext = _authorizationContext; filter.OnAuthorization(authorizationContext); if (authorizationContext.Result != null) { goto case State.AuthorizationShortCircuit; } goto case State.AuthorizationNext; } case State.AuthorizationShortCircuit: { Debug.Assert(_authorizationContext?.Result != null); _result = _authorizationContext.Result; authenticationChallengeBypassResultFilters = true; goto case State.AuthenticationChallengeBegin; } case State.AuthorizationEnd: { goto case State.ExceptionBegin; } case State.ExceptionBegin: { _cursor.Reset(); goto case State.ExceptionNext; } case State.ExceptionNext: { var current = _cursor.GetNextFilter <IExceptionFilter, IAsyncExceptionFilter>(); if (current.FilterAsync != null) { state = current.FilterAsync; goto case State.ExceptionAsyncBegin; } else if (current.Filter != null) { state = current.Filter; goto case State.ExceptionSyncBegin; } else if (scope == Scope.Exception) { // All exception filters are on the stack already - so execute the 'inside'. goto case State.ExceptionInside; } else { // There are no exception filters - so jump right to 'inside'. Debug.Assert(scope == Scope.Invoker); goto case State.ActionBegin; } } case State.ExceptionAsyncBegin: { var task = InvokeNextExceptionFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ExceptionAsyncResume; return(task); } goto case State.ExceptionAsyncResume; } case State.ExceptionAsyncResume: { Debug.Assert(state != null); var filter = (IAsyncExceptionFilter)state; var exceptionContext = _exceptionContext; // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception, // we'll call the filter. Otherwise there's nothing to do. if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled) { var task = filter.OnExceptionAsync(exceptionContext); if (task.Status != TaskStatus.RanToCompletion) { next = State.ExceptionAsyncEnd; return(task); } goto case State.ExceptionAsyncEnd; } goto case State.ExceptionEnd; } case State.ExceptionAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_exceptionContext != null); var filter = (IAsyncExceptionFilter)state; var exceptionContext = _exceptionContext; if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled) { // We don't need to do anthing to trigger a short circuit. If there's another // exception filter on the stack it will check the same set of conditions // and then just skip itself. } goto case State.ExceptionEnd; } case State.ExceptionSyncBegin: { var task = InvokeNextExceptionFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ExceptionSyncEnd; return(task); } goto case State.ExceptionSyncEnd; } case State.ExceptionSyncEnd: { Debug.Assert(state != null); var filter = (IExceptionFilter)state; var exceptionContext = _exceptionContext; // When we get here we're 'unwinding' the stack of exception filters. If we have an unhandled exception, // we'll call the filter. Otherwise there's nothing to do. if (exceptionContext?.Exception != null && !exceptionContext.ExceptionHandled) { filter.OnException(exceptionContext); if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled) { // We don't need to do anthing to trigger a short circuit. If there's another // exception filter on the stack it will check the same set of conditions // and then just skip itself. } } goto case State.ExceptionEnd; } case State.ExceptionInside: { goto case State.ActionBegin; } case State.ExceptionHandled: { // We arrive in this state when an exception happened, but was handled by exception filters // either by setting ExceptionHandled, or nulling out the Exception or setting a result // on the ExceptionContext. // // We need to execute the result (if any) and then exit gracefully which unwinding Resource // filters. Debug.Assert(state != null); Debug.Assert(_exceptionContext != null); if (_exceptionContext.Result == null) { _exceptionContext.Result = new EmptyResult(); } if (scope == Scope.Invoker) { Debug.Assert(_exceptionContext.Result != null); _result = _exceptionContext.Result; } InvokeActionResult(_controllerContext, _result); resultExecuted = true; goto case State.InvokeEnd; } case State.ExceptionEnd: { var exceptionContext = _exceptionContext; if (scope == Scope.Exception) { isCompleted = true; return(TaskHelpers.CompletedTask); } if (exceptionContext != null) { if (exceptionContext.Exception == null || exceptionContext.ExceptionHandled) { goto case State.ExceptionHandled; } Rethrow(exceptionContext); Debug.Fail("unreachable"); } if (resultExecuted) { // MVC5 wraps the result in the Exception filter scope // MVC Core does not // This flag indicates what to do at this point after we unwrap from invoking the action goto case State.InvokeEnd; } goto case State.ResultBegin; } case State.ActionBegin: { if (_controllerContext.Controller.ValidateRequest) { ValidateRequest(_controllerContext); } _cursor.Reset(); goto case State.ActionNext; } case State.ActionNext: { var current = _cursor.GetNextFilter <IActionFilter, IAsyncActionFilter>(); if (current.FilterAsync != null) { if (_actionExecutingContext == null) { _actionExecutingContext = new ActionExecutingContext(_controllerContext, _actionDescriptor, _parameters); } state = current.FilterAsync; goto case State.ActionAsyncBegin; } else if (current.Filter != null) { if (_actionExecutingContext == null) { _actionExecutingContext = new ActionExecutingContext(_controllerContext, _actionDescriptor, _parameters); } state = current.Filter; goto case State.ActionSyncBegin; } else { goto case State.ActionInside; } } case State.ActionAsyncBegin: { Debug.Assert(state != null); Debug.Assert(_actionExecutingContext != null); var filter = (IAsyncActionFilter)state; var actionExecutingContext = _actionExecutingContext; var task = filter.OnActionExecutionAsync(actionExecutingContext, InvokeNextActionFilterAwaitedAsync); if (task.Status != TaskStatus.RanToCompletion) { next = State.ActionAsyncEnd; return(task); } goto case State.ActionAsyncEnd; } case State.ActionAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_actionExecutingContext != null); var filter = (IAsyncActionFilter)state; if (_actionExecutedContext == null) { // If we get here then the filter didn't call 'next' indicating a short circuit. _actionExecutedContext = new ActionExecutedContextEx(_controllerContext, _actionDescriptor, canceled: true, exception: null) { Result = _actionExecutingContext.Result, }; } goto case State.ActionEnd; } case State.ActionSyncBegin: { Debug.Assert(state != null); Debug.Assert(_actionExecutingContext != null); var filter = (IActionFilter)state; var actionExecutingContext = _actionExecutingContext; filter.OnActionExecuting(actionExecutingContext); if (actionExecutingContext.Result != null) { // Short-circuited by setting a result. _actionExecutedContext = new ActionExecutedContextEx(_controllerContext, _actionDescriptor, canceled: true, exception: null) { Result = _actionExecutingContext.Result, }; goto case State.ActionEnd; } var task = InvokeNextActionFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ActionSyncEnd; return(task); } goto case State.ActionSyncEnd; } case State.ActionSyncEnd: { Debug.Assert(state != null); Debug.Assert(_actionExecutingContext != null); Debug.Assert(_actionExecutedContext != null); var filter = (IActionFilter)state; var actionExecutedContext = _actionExecutedContext; filter.OnActionExecuted(actionExecutedContext); goto case State.ActionEnd; } case State.ActionInside: { var task = InvokeActionMethodAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ActionEnd; return(task); } goto case State.ActionEnd; } case State.ActionEnd: { if (scope == Scope.Action) { if (_actionExecutedContext == null) { _actionExecutedContext = new ActionExecutedContextEx(_controllerContext, _actionDescriptor, canceled: false, exception: null) { Result = _result, }; } isCompleted = true; return(TaskHelpers.CompletedTask); } var actionExecutedContext = _actionExecutedContext; Rethrow(actionExecutedContext); if (actionExecutedContext != null) { _result = actionExecutedContext.Result; } goto case State.AuthenticationChallengeBegin; } case State.ResultBegin: { _cursor.Reset(); goto case State.ResultNext; } case State.ResultNext: { var current = _cursor.GetNextFilter <IResultFilter, IAsyncResultFilter>(); if (current.FilterAsync != null) { if (_resultExecutingContext == null) { _resultExecutingContext = new ResultExecutingContext(_controllerContext, _result); } state = current.FilterAsync; goto case State.ResultAsyncBegin; } else if (current.Filter != null) { if (_resultExecutingContext == null) { _resultExecutingContext = new ResultExecutingContext(_controllerContext, _result); } state = current.Filter; goto case State.ResultSyncBegin; } else { goto case State.ResultInside; } } case State.ResultAsyncBegin: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); var filter = (IAsyncResultFilter)state; var resultExecutingContext = _resultExecutingContext; var task = filter.OnResultExecutionAsync(resultExecutingContext, InvokeNextResultFilterAwaitedAsync); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResultAsyncEnd; return(task); } goto case State.ResultAsyncEnd; } case State.ResultAsyncEnd: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); var filter = (IAsyncResultFilter)state; var resultExecutingContext = _resultExecutingContext; var resultExecutedContext = _resultExecutedContext; if (resultExecutedContext == null || resultExecutingContext.Cancel == true) { // Short-circuited by not calling next || Short-circuited by setting Cancel == true _resultExecutedContext = new ResultExecutedContextEx(_controllerContext, resultExecutingContext.Result, canceled: true, exception: null); } goto case State.ResultEnd; } case State.ResultSyncBegin: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); var filter = (IResultFilter)state; var resultExecutingContext = _resultExecutingContext; filter.OnResultExecuting(resultExecutingContext); if (_resultExecutingContext.Cancel == true) { // Short-circuited by setting Cancel == true _resultExecutedContext = new ResultExecutedContextEx(_controllerContext, resultExecutingContext.Result, canceled: true, exception: null); goto case State.ResultEnd; } var task = InvokeNextResultFilterAsync(); if (task.Status != TaskStatus.RanToCompletion) { next = State.ResultSyncEnd; return(task); } goto case State.ResultSyncEnd; } case State.ResultSyncEnd: { Debug.Assert(state != null); Debug.Assert(_resultExecutingContext != null); Debug.Assert(_resultExecutedContext != null); var filter = (IResultFilter)state; var resultExecutedContext = _resultExecutedContext; filter.OnResultExecuted(resultExecutedContext); goto case State.ResultEnd; } case State.ResultInside: { // If we executed result filters then we need to grab the result from there. if (_resultExecutingContext != null) { _result = _resultExecutingContext.Result; } if (_result == null) { // The empty result is always flowed back as the 'executed' result if we don't have one. _result = new EmptyResult(); } InvokeActionResult(_controllerContext, _result); resultExecuted = true; goto case State.ResultEnd; } case State.ResultEnd: { var result = _result; if (scope == Scope.Result) { if (_resultExecutedContext == null) { _resultExecutedContext = new ResultExecutedContextEx(_controllerContext, result, canceled: false, exception: null); } isCompleted = true; return(TaskHelpers.CompletedTask); } Rethrow(_resultExecutedContext); goto case State.InvokeEnd; } case State.InvokeEnd: { isCompleted = true; return(TaskHelpers.CompletedTask); } default: throw new InvalidOperationException(); } }