Move exception and result filters in base
This commit is contained in:
parent
e7bd6cfc06
commit
84e007a2a7
|
|
@ -18,18 +18,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
private readonly ControllerActionInvokerCacheEntry _cacheEntry;
|
||||
private readonly ControllerContext _controllerContext;
|
||||
|
||||
private object _controller;
|
||||
|
||||
private Dictionary<string, object> _arguments;
|
||||
|
||||
private ExceptionContext _exceptionContext;
|
||||
|
||||
private ActionExecutingContext _actionExecutingContext;
|
||||
private ActionExecutedContext _actionExecutedContext;
|
||||
|
||||
private ResultExecutingContext _resultExecutingContext;
|
||||
private ResultExecutedContext _resultExecutedContext;
|
||||
|
||||
internal ControllerActionInvoker(
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
|
|
@ -52,9 +46,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
protected override void ReleaseResources()
|
||||
{
|
||||
if (_controller != null && _cacheEntry.ControllerReleaser != null)
|
||||
if (_instance != null && _cacheEntry.ControllerReleaser != null)
|
||||
{
|
||||
_cacheEntry.ControllerReleaser(_controllerContext, _controller);
|
||||
_cacheEntry.ControllerReleaser(_controllerContext, _instance);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,214 +59,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
switch (next)
|
||||
{
|
||||
case State.ResourceInsideBegin:
|
||||
{
|
||||
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.Resource);
|
||||
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)
|
||||
{
|
||||
_diagnosticSource.BeforeOnExceptionAsync(exceptionContext, filter);
|
||||
|
||||
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;
|
||||
|
||||
_diagnosticSource.AfterOnExceptionAsync(exceptionContext, filter);
|
||||
|
||||
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.
|
||||
_logger.ExceptionFilterShortCircuited(filter);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_diagnosticSource.BeforeOnException(exceptionContext, filter);
|
||||
|
||||
filter.OnException(exceptionContext);
|
||||
|
||||
_diagnosticSource.AfterOnException(exceptionContext, filter);
|
||||
|
||||
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.
|
||||
_logger.ExceptionFilterShortCircuited(filter);
|
||||
}
|
||||
}
|
||||
|
||||
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.Resource)
|
||||
{
|
||||
Debug.Assert(_exceptionContext.Result != null);
|
||||
_result = _exceptionContext.Result;
|
||||
}
|
||||
|
||||
var task = InvokeResultAsync(_exceptionContext.Result);
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.ResourceInsideEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.ResourceInsideEnd;
|
||||
}
|
||||
|
||||
case State.ExceptionEnd:
|
||||
{
|
||||
var exceptionContext = _exceptionContext;
|
||||
|
||||
if (scope == Scope.Exception)
|
||||
{
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
if (exceptionContext != null)
|
||||
{
|
||||
if (exceptionContext.Result != null ||
|
||||
exceptionContext.Exception == null ||
|
||||
exceptionContext.ExceptionHandled)
|
||||
{
|
||||
goto case State.ExceptionHandled;
|
||||
}
|
||||
|
||||
Rethrow(exceptionContext);
|
||||
Debug.Fail("unreachable");
|
||||
}
|
||||
|
||||
goto case State.ResultBegin;
|
||||
}
|
||||
|
||||
case State.ActionBegin:
|
||||
{
|
||||
var controllerContext = _controllerContext;
|
||||
|
||||
_cursor.Reset();
|
||||
|
||||
_controller = _cacheEntry.ControllerFactory(controllerContext);
|
||||
_instance = _cacheEntry.ControllerFactory(controllerContext);
|
||||
|
||||
_arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
|
|
@ -293,7 +86,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
if (_actionExecutingContext == null)
|
||||
{
|
||||
_actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _controller);
|
||||
_actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _instance);
|
||||
}
|
||||
|
||||
state = current.FilterAsync;
|
||||
|
|
@ -303,7 +96,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
if (_actionExecutingContext == null)
|
||||
{
|
||||
_actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _controller);
|
||||
_actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _instance);
|
||||
}
|
||||
|
||||
state = current.Filter;
|
||||
|
|
@ -350,7 +143,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_controllerContext,
|
||||
_filters,
|
||||
_controller)
|
||||
_instance)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _actionExecutingContext.Result,
|
||||
|
|
@ -384,7 +177,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
_controller)
|
||||
_instance)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _actionExecutingContext.Result,
|
||||
|
|
@ -439,7 +232,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
if (_actionExecutedContext == null)
|
||||
{
|
||||
_actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _controller)
|
||||
_actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _instance)
|
||||
{
|
||||
Result = _result,
|
||||
};
|
||||
|
|
@ -457,206 +250,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
_result = actionExecutedContext.Result;
|
||||
}
|
||||
|
||||
if (scope == Scope.Exception)
|
||||
{
|
||||
// If we're inside an exception filter, let's allow those filters to 'unwind' before
|
||||
// the result.
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
Debug.Assert(scope == Scope.Resource);
|
||||
goto case State.ResultBegin;
|
||||
}
|
||||
|
||||
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, _filters, _result, _controller);
|
||||
}
|
||||
|
||||
state = current.FilterAsync;
|
||||
goto case State.ResultAsyncBegin;
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
if (_resultExecutingContext == null)
|
||||
{
|
||||
_resultExecutingContext = new ResultExecutingContext(_controllerContext, _filters, _result, _controller);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecution(resultExecutingContext, filter);
|
||||
|
||||
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
|
||||
_logger.ResourceFilterShortCircuited(filter);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_controllerContext,
|
||||
_filters,
|
||||
resultExecutingContext.Result,
|
||||
_controller)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnResultExecution(_resultExecutedContext, filter);
|
||||
goto case State.ResultEnd;
|
||||
}
|
||||
|
||||
case State.ResultSyncBegin:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
|
||||
var filter = (IResultFilter)state;
|
||||
var resultExecutingContext = _resultExecutingContext;
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecuting(resultExecutingContext, filter);
|
||||
|
||||
filter.OnResultExecuting(resultExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuting(resultExecutingContext, filter);
|
||||
|
||||
if (_resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// Short-circuited by setting Cancel == true
|
||||
_logger.ResourceFilterShortCircuited(filter);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
resultExecutingContext,
|
||||
_filters,
|
||||
resultExecutingContext.Result,
|
||||
_controller)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecuted(resultExecutedContext, filter);
|
||||
|
||||
filter.OnResultExecuted(resultExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuted(resultExecutedContext, filter);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
var task = InvokeResultAsync(_result);
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.ResultEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.ResultEnd;
|
||||
}
|
||||
|
||||
case State.ResultEnd:
|
||||
{
|
||||
var result = _result;
|
||||
|
||||
if (scope == Scope.Result)
|
||||
{
|
||||
if (_resultExecutedContext == null)
|
||||
{
|
||||
_resultExecutedContext = new ResultExecutedContext(_controllerContext, _filters, result, _controller);
|
||||
}
|
||||
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
Rethrow(_resultExecutedContext);
|
||||
|
||||
goto case State.ResourceInsideEnd;
|
||||
}
|
||||
|
||||
case State.ResourceInsideEnd:
|
||||
{
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
|
@ -666,28 +259,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private async Task InvokeNextExceptionFilterAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var next = State.ExceptionNext;
|
||||
var state = (object)null;
|
||||
var scope = Scope.Exception;
|
||||
var isCompleted = false;
|
||||
while (!isCompleted)
|
||||
{
|
||||
await Next(ref next, ref scope, ref state, ref isCompleted);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_exceptionContext = new ExceptionContext(_controllerContext, _filters)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InvokeNextActionFilterAsync()
|
||||
{
|
||||
try
|
||||
|
|
@ -703,7 +274,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _controller)
|
||||
_actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _instance)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception),
|
||||
};
|
||||
|
|
@ -737,7 +308,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
var controllerContext = _controllerContext;
|
||||
var executor = _cacheEntry.ActionMethodExecutor;
|
||||
var controller = _controller;
|
||||
var controller = _instance;
|
||||
var arguments = _arguments;
|
||||
var orderedArguments = PrepareArguments(arguments, executor);
|
||||
|
||||
|
|
@ -842,58 +413,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return typeof(IActionResult).IsAssignableFrom(resultType);
|
||||
}
|
||||
|
||||
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 ResultExecutedContext(_controllerContext, _filters, _result, _controller)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception),
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task<ResultExecutedContext> InvokeNextResultFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
if (_resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// If we get here, it means that an async filter set cancel == true AND called next().
|
||||
// This is forbidden.
|
||||
var message = Resources.FormatAsyncResultFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncResultFilter).Name,
|
||||
nameof(ResultExecutingContext.Cancel),
|
||||
typeof(ResultExecutingContext).Name,
|
||||
typeof(ResultExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
await InvokeNextResultFilterAsync();
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
return _resultExecutedContext;
|
||||
}
|
||||
|
||||
/// <remarks><see cref="ResourceInvoker.InvokeFilterPipelineAsync"/> for details on what the
|
||||
/// variables in this method represent.</remarks>
|
||||
protected override async Task InvokeInnerFilterAsync()
|
||||
{
|
||||
var next = State.ResourceInsideBegin;
|
||||
var scope = Scope.Resource;
|
||||
var next = State.ActionBegin;
|
||||
var scope = Scope.Invoker;
|
||||
var state = (object)null;
|
||||
var isCompleted = false;
|
||||
|
||||
|
|
@ -903,29 +428,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private static void Rethrow(ExceptionContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionDispatchInfo != null)
|
||||
{
|
||||
context.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
|
||||
if (context.Exception != null)
|
||||
{
|
||||
throw context.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Rethrow(ActionExecutedContext context)
|
||||
{
|
||||
if (context == null)
|
||||
|
|
@ -949,29 +451,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private static void Rethrow(ResultExecutedContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionDispatchInfo != null)
|
||||
{
|
||||
context.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
|
||||
if (context.Exception != null)
|
||||
{
|
||||
throw context.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
private Task BindArgumentsAsync()
|
||||
{
|
||||
// Perf: Avoid allocating async state machines where possible. We only need the state
|
||||
|
|
@ -984,7 +463,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
Debug.Assert(_cacheEntry.ControllerBinderDelegate != null);
|
||||
return _cacheEntry.ControllerBinderDelegate(_controllerContext, _controller, _arguments);
|
||||
return _cacheEntry.ControllerBinderDelegate(_controllerContext, _instance, _arguments);
|
||||
}
|
||||
|
||||
private static object[] PrepareArguments(
|
||||
|
|
@ -1016,25 +495,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
private enum Scope
|
||||
{
|
||||
Resource,
|
||||
Exception,
|
||||
Invoker,
|
||||
Action,
|
||||
Result,
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
ResourceInsideBegin,
|
||||
ExceptionBegin,
|
||||
ExceptionNext,
|
||||
ExceptionAsyncBegin,
|
||||
ExceptionAsyncResume,
|
||||
ExceptionAsyncEnd,
|
||||
ExceptionSyncBegin,
|
||||
ExceptionSyncEnd,
|
||||
ExceptionInside,
|
||||
ExceptionHandled,
|
||||
ExceptionEnd,
|
||||
ActionBegin,
|
||||
ActionNext,
|
||||
ActionAsyncBegin,
|
||||
|
|
@ -1043,15 +509,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
ActionSyncEnd,
|
||||
ActionInside,
|
||||
ActionEnd,
|
||||
ResultBegin,
|
||||
ResultNext,
|
||||
ResultAsyncBegin,
|
||||
ResultAsyncEnd,
|
||||
ResultSyncBegin,
|
||||
ResultSyncEnd,
|
||||
ResultInside,
|
||||
ResultEnd,
|
||||
ResourceInsideEnd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
private AuthorizationFilterContext _authorizationContext;
|
||||
private ResourceExecutingContext _resourceExecutingContext;
|
||||
private ResourceExecutedContext _resourceExecutedContext;
|
||||
private ExceptionContext _exceptionContext;
|
||||
private ResultExecutingContext _resultExecutingContext;
|
||||
private ResultExecutedContext _resultExecutedContext;
|
||||
|
||||
// Do not make this readonly, it's mutable. We don't want to make a copy.
|
||||
// https://blogs.msdn.microsoft.com/ericlippert/2008/05/14/mutating-readonly-structs/
|
||||
protected FilterCursor _cursor;
|
||||
protected IActionResult _result;
|
||||
protected object _instance;
|
||||
|
||||
public ResourceInvoker(
|
||||
DiagnosticSource diagnosticSource,
|
||||
|
|
@ -313,18 +317,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
state = current.Filter;
|
||||
goto case State.ResourceSyncBegin;
|
||||
}
|
||||
else if (scope == Scope.Resource)
|
||||
{
|
||||
// All resource filters are currently on the stack - now execute the 'inside'.
|
||||
Debug.Assert(_resourceExecutingContext != null);
|
||||
goto case State.ResourceInside;
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no resource filters - so jump right to 'inside'.
|
||||
Debug.Assert(scope == Scope.Invoker);
|
||||
next = State.InvokeEnd;
|
||||
return InvokeInnerFilterAsync();
|
||||
// All resource filters are currently on the stack - now execute the 'inside'.
|
||||
goto case State.ResourceInside;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -447,11 +443,419 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
case State.ResourceInside:
|
||||
{
|
||||
goto case State.ExceptionBegin;
|
||||
}
|
||||
|
||||
case State.ExceptionBegin:
|
||||
{
|
||||
next = State.ResourceOutside;
|
||||
return InvokeInnerFilterAsync();
|
||||
_cursor.Reset();
|
||||
goto case State.ExceptionNext;
|
||||
}
|
||||
case State.ResourceOutside:
|
||||
|
||||
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 the action.
|
||||
Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource);
|
||||
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)
|
||||
{
|
||||
_diagnosticSource.BeforeOnExceptionAsync(exceptionContext, filter);
|
||||
|
||||
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;
|
||||
|
||||
_diagnosticSource.AfterOnExceptionAsync(exceptionContext, filter);
|
||||
|
||||
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.
|
||||
_logger.ExceptionFilterShortCircuited(filter);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_diagnosticSource.BeforeOnException(exceptionContext, filter);
|
||||
|
||||
filter.OnException(exceptionContext);
|
||||
|
||||
_diagnosticSource.AfterOnException(exceptionContext, filter);
|
||||
|
||||
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.
|
||||
_logger.ExceptionFilterShortCircuited(filter);
|
||||
}
|
||||
}
|
||||
|
||||
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.Resource)
|
||||
{
|
||||
Debug.Assert(_exceptionContext.Result != null);
|
||||
_result = _exceptionContext.Result;
|
||||
}
|
||||
|
||||
var task = InvokeResultAsync(_exceptionContext.Result);
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.ResourceInsideEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.ResourceInsideEnd;
|
||||
}
|
||||
|
||||
case State.ExceptionEnd:
|
||||
{
|
||||
var exceptionContext = _exceptionContext;
|
||||
|
||||
if (scope == Scope.Exception)
|
||||
{
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
if (exceptionContext != null)
|
||||
{
|
||||
if (exceptionContext.Result != null ||
|
||||
exceptionContext.Exception == null ||
|
||||
exceptionContext.ExceptionHandled)
|
||||
{
|
||||
goto case State.ExceptionHandled;
|
||||
}
|
||||
|
||||
Rethrow(exceptionContext);
|
||||
Debug.Fail("unreachable");
|
||||
}
|
||||
|
||||
goto case State.ResultBegin;
|
||||
}
|
||||
|
||||
case State.ActionBegin:
|
||||
{
|
||||
var task = InvokeInnerFilterAsync();
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.ActionEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.ActionEnd;
|
||||
}
|
||||
|
||||
case State.ActionEnd:
|
||||
{
|
||||
if (scope == Scope.Exception)
|
||||
{
|
||||
// If we're inside an exception filter, let's allow those filters to 'unwind' before
|
||||
// the result.
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
Debug.Assert(scope == Scope.Invoker || scope == Scope.Resource);
|
||||
goto case State.ResultBegin;
|
||||
}
|
||||
|
||||
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(_actionContext, _filters, _result, _instance);
|
||||
}
|
||||
|
||||
state = current.FilterAsync;
|
||||
goto case State.ResultAsyncBegin;
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
if (_resultExecutingContext == null)
|
||||
{
|
||||
_resultExecutingContext = new ResultExecutingContext(_actionContext, _filters, _result, _instance);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecution(resultExecutingContext, filter);
|
||||
|
||||
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
|
||||
_logger.ResourceFilterShortCircuited(filter);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_actionContext,
|
||||
_filters,
|
||||
resultExecutingContext.Result,
|
||||
_instance)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnResultExecution(_resultExecutedContext, filter);
|
||||
goto case State.ResultEnd;
|
||||
}
|
||||
|
||||
case State.ResultSyncBegin:
|
||||
{
|
||||
Debug.Assert(state != null);
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
|
||||
var filter = (IResultFilter)state;
|
||||
var resultExecutingContext = _resultExecutingContext;
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecuting(resultExecutingContext, filter);
|
||||
|
||||
filter.OnResultExecuting(resultExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuting(resultExecutingContext, filter);
|
||||
|
||||
if (_resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// Short-circuited by setting Cancel == true
|
||||
_logger.ResourceFilterShortCircuited(filter);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
resultExecutingContext,
|
||||
_filters,
|
||||
resultExecutingContext.Result,
|
||||
_instance)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecuted(resultExecutedContext, filter);
|
||||
|
||||
filter.OnResultExecuted(resultExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuted(resultExecutedContext, filter);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
var task = InvokeResultAsync(_result);
|
||||
if (task.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
next = State.ResultEnd;
|
||||
return task;
|
||||
}
|
||||
|
||||
goto case State.ResultEnd;
|
||||
}
|
||||
|
||||
case State.ResultEnd:
|
||||
{
|
||||
var result = _result;
|
||||
|
||||
if (scope == Scope.Result)
|
||||
{
|
||||
if (_resultExecutedContext == null)
|
||||
{
|
||||
_resultExecutedContext = new ResultExecutedContext(_actionContext, _filters, result, _instance);
|
||||
}
|
||||
|
||||
isCompleted = true;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
Rethrow(_resultExecutedContext);
|
||||
|
||||
goto case State.ResourceInsideEnd;
|
||||
}
|
||||
|
||||
case State.ResourceInsideEnd:
|
||||
{
|
||||
var result = _result;
|
||||
|
||||
|
|
@ -538,6 +942,74 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Debug.Assert(_resourceExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task InvokeNextExceptionFilterAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var next = State.ExceptionNext;
|
||||
var state = (object)null;
|
||||
var scope = Scope.Exception;
|
||||
var isCompleted = false;
|
||||
while (!isCompleted)
|
||||
{
|
||||
await Next(ref next, ref scope, ref state, ref isCompleted);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_exceptionContext = new ExceptionContext(_actionContext, _filters)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(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 ResultExecutedContext(_actionContext, _filters, _result, _instance)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception),
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task<ResultExecutedContext> InvokeNextResultFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
if (_resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// If we get here, it means that an async filter set cancel == true AND called next().
|
||||
// This is forbidden.
|
||||
var message = Resources.FormatAsyncResultFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncResultFilter).Name,
|
||||
nameof(ResultExecutingContext.Cancel),
|
||||
typeof(ResultExecutingContext).Name,
|
||||
typeof(ResultExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
await InvokeNextResultFilterAsync();
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
return _resultExecutedContext;
|
||||
}
|
||||
|
||||
private static void Rethrow(ResourceExecutedContext context)
|
||||
{
|
||||
if (context == null)
|
||||
|
|
@ -561,10 +1033,58 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private static void Rethrow(ExceptionContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionDispatchInfo != null)
|
||||
{
|
||||
context.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
|
||||
if (context.Exception != null)
|
||||
{
|
||||
throw context.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Rethrow(ResultExecutedContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.ExceptionDispatchInfo != null)
|
||||
{
|
||||
context.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
|
||||
if (context.Exception != null)
|
||||
{
|
||||
throw context.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Scope
|
||||
{
|
||||
Invoker,
|
||||
Resource,
|
||||
Exception,
|
||||
Result,
|
||||
}
|
||||
|
||||
private enum State
|
||||
|
|
@ -585,8 +1105,28 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
ResourceSyncEnd,
|
||||
ResourceShortCircuit,
|
||||
ResourceInside,
|
||||
ResourceOutside,
|
||||
ResourceInsideEnd,
|
||||
ResourceEnd,
|
||||
ExceptionBegin,
|
||||
ExceptionNext,
|
||||
ExceptionAsyncBegin,
|
||||
ExceptionAsyncResume,
|
||||
ExceptionAsyncEnd,
|
||||
ExceptionSyncBegin,
|
||||
ExceptionSyncEnd,
|
||||
ExceptionInside,
|
||||
ExceptionHandled,
|
||||
ExceptionEnd,
|
||||
ActionBegin,
|
||||
ActionEnd,
|
||||
ResultBegin,
|
||||
ResultNext,
|
||||
ResultAsyncBegin,
|
||||
ResultAsyncEnd,
|
||||
ResultSyncBegin,
|
||||
ResultSyncEnd,
|
||||
ResultInside,
|
||||
ResultEnd,
|
||||
InvokeEnd,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
public class ControllerActionInvokerTest : CommonResourceInvokerTest
|
||||
{
|
||||
#region Diagnostics
|
||||
|
||||
[Fact]
|
||||
public async Task Invoke_Success_LogsCorrectValues()
|
||||
{
|
||||
|
|
@ -142,6 +144,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Assert.NotNull(listener.AfterAction?.HttpContext);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller Context
|
||||
|
||||
[Fact]
|
||||
public async Task AddingValueProviderFactory_AtResourceFilter_IsAvailableInControllerContext()
|
||||
|
|
@ -204,6 +209,599 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Assert.Same(valueProviderFactory2, controllerContext.ValueProviderFactories[0]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Action Filters
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
IActionResult result = null;
|
||||
|
||||
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => result = c.Result)
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(filter.Object);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.Same(Result, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
IActionResult result = null;
|
||||
|
||||
var filter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
filter
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (context, next) =>
|
||||
{
|
||||
var resultContext = await next();
|
||||
result = resultContext.Result;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(filter.Object);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
Assert.Same(Result, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_ShortCircuit()
|
||||
{
|
||||
// Arrange
|
||||
var result = new Mock<IActionResult>(MockBehavior.Strict);
|
||||
result
|
||||
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var actionFilter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => context = c)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter2 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>(c => c.Result = result.Object)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter3 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
{
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
actionFilter3.Object,
|
||||
resultFilter.Object,
|
||||
});
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
result.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
actionFilter2.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter2.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Never());
|
||||
|
||||
resultFilter.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.True(context.Canceled);
|
||||
Assert.Same(context.Result, result.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_ShortCircuit_WithResult()
|
||||
{
|
||||
// Arrange
|
||||
var result = new Mock<IActionResult>(MockBehavior.Strict);
|
||||
result
|
||||
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var actionFilter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => context = c)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter2 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>((c, next) =>
|
||||
{
|
||||
// Notice we're not calling next
|
||||
c.Result = result.Object;
|
||||
return Task.FromResult(true);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter3 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
|
||||
var resultFilter1 = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter1.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter1.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
var resultFilter2 = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter2.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter2.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
{
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
actionFilter3.Object,
|
||||
resultFilter1.Object,
|
||||
resultFilter2.Object,
|
||||
});
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
result.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
actionFilter2.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
resultFilter1.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter1.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
resultFilter2.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter2.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.True(context.Canceled);
|
||||
Assert.Same(context.Result, result.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_ShortCircuit_WithoutResult()
|
||||
{
|
||||
// Arrange
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var actionFilter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => context = c)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter2 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>((c, next) =>
|
||||
{
|
||||
// Notice we're not calling next
|
||||
return Task.FromResult(true);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter3 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
{
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
actionFilter3.Object,
|
||||
resultFilter.Object,
|
||||
});
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
actionFilter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
actionFilter2.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
resultFilter.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.True(context.Canceled);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_ShortCircuit_WithResult_CallNext()
|
||||
{
|
||||
// Arrange
|
||||
var actionFilter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
c.Result = new EmptyResult();
|
||||
await next();
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var message =
|
||||
"If an IAsyncActionFilter provides a result value by setting the Result property of " +
|
||||
"ActionExecutingContext to a non-null value, then it cannot call the next filter by invoking " +
|
||||
"ActionExecutionDelegate.";
|
||||
|
||||
var invoker = CreateInvoker(actionFilter.Object);
|
||||
|
||||
// Act & Assert
|
||||
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await invoker.InvokeAsync(),
|
||||
message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_WithExceptionThrownByAction()
|
||||
{
|
||||
// Arrange
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c =>
|
||||
{
|
||||
context = c;
|
||||
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(c.ExceptionHandled);
|
||||
c.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(filter.Object, exception: Exception);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.Same(Exception, context.Exception);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_WithExceptionThrownByActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var exception = new DataMisalignedException();
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var filter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c =>
|
||||
{
|
||||
context = c;
|
||||
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(c.ExceptionHandled);
|
||||
c.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var filter2 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter2
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>(c => { throw exception; })
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new[] { filter1.Object, filter2.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
filter2.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter2.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Never());
|
||||
|
||||
Assert.Same(exception, context.Exception);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_WithExceptionThrownByActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var exception = new DataMisalignedException();
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var filter1 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
filter1
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(context.ExceptionHandled);
|
||||
context.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var filter2 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter2.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter2
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => { throw exception; })
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { filter1.Object, filter2.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter1.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
filter2.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
|
||||
Assert.Same(exception, context.Exception);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_HandleException()
|
||||
{
|
||||
// Arrange
|
||||
var result = new Mock<IActionResult>(MockBehavior.Strict);
|
||||
result
|
||||
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
||||
.Returns<ActionContext>((context) => Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c =>
|
||||
{
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(c.ExceptionHandled);
|
||||
c.ExceptionHandled = true;
|
||||
|
||||
c.Result = result.Object;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
new IFilterMetadata[] { actionFilter.Object, resultFilter.Object },
|
||||
exception: Exception);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
actionFilter.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
resultFilter.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
result.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_WithActionResult_FromActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = Mock.Of<IActionResult>();
|
||||
|
||||
ResourceExecutedContext context = null;
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>((c) =>
|
||||
{
|
||||
c.Result = expected;
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { resourceFilter.Object, actionFilter.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, context.Result);
|
||||
|
||||
resourceFilter.Verify(
|
||||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_HandleException_FromActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new DataMisalignedException();
|
||||
|
||||
ResourceExecutedContext context = null;
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
context.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>((c) =>
|
||||
{
|
||||
throw expected;
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { resourceFilter.Object, actionFilter.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, context.Exception);
|
||||
Assert.Same(expected, context.ExceptionDispatchInfo.SourceException);
|
||||
|
||||
resourceFilter.Verify(
|
||||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_HandlesException_FromExceptionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new DataMisalignedException();
|
||||
|
||||
ResourceExecutedContext context = null;
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
context.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var exceptionFilter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
||||
exceptionFilter
|
||||
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
||||
.Callback<ExceptionContext>((c) =>
|
||||
{
|
||||
throw expected;
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { resourceFilter.Object, exceptionFilter.Object }, exception: Exception);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, context.Exception);
|
||||
Assert.Same(expected, context.ExceptionDispatchInfo.SourceException);
|
||||
|
||||
resourceFilter.Verify(
|
||||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_ExceptionBubbling_AsyncActionFilter_To_ResourceFilter()
|
||||
{
|
||||
// Arrange
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
var context = await next();
|
||||
Assert.Same(Exception, context.Exception);
|
||||
context.ExceptionHandled = true;
|
||||
});
|
||||
|
||||
var actionFilter1 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
await next();
|
||||
});
|
||||
|
||||
var actionFilter2 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
await next();
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
new IFilterMetadata[]
|
||||
{
|
||||
resourceFilter.Object,
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
},
|
||||
// The action won't run
|
||||
exception: Exception);
|
||||
|
||||
// Act & Assert
|
||||
await invoker.InvokeAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Action Method Signatures
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_AsyncAction_TaskReturnType()
|
||||
{
|
||||
|
|
@ -794,6 +1392,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Assert.Equal(5, context.Object.Items["Result"]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override ResourceInvoker CreateInvoker(
|
||||
IFilterMetadata[] filters,
|
||||
Exception exception = null,
|
||||
|
|
|
|||
|
|
@ -3,20 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -701,7 +692,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
// None of these filters should run
|
||||
var authorizationFilter2 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
||||
var resourceFilter = new Mock<IResourceFilter>(MockBehavior.Strict);
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
|
|
@ -710,7 +700,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
authorizationFilter1.Object,
|
||||
authorizationFilter2.Object,
|
||||
resourceFilter.Object,
|
||||
actionFilter.Object,
|
||||
resultFilter.Object,
|
||||
});
|
||||
|
||||
|
|
@ -751,432 +740,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
challenge.Verify(c => c.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
IActionResult result = null;
|
||||
|
||||
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => result = c.Result)
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(filter.Object);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.Same(Result, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
IActionResult result = null;
|
||||
|
||||
var filter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
filter
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (context, next) =>
|
||||
{
|
||||
var resultContext = await next();
|
||||
result = resultContext.Result;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(filter.Object);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
Assert.Same(Result, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_ShortCircuit()
|
||||
{
|
||||
// Arrange
|
||||
var result = new Mock<IActionResult>(MockBehavior.Strict);
|
||||
result
|
||||
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var actionFilter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => context = c)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter2 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>(c => c.Result = result.Object)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter3 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
{
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
actionFilter3.Object,
|
||||
resultFilter.Object,
|
||||
});
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
result.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
actionFilter2.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter2.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Never());
|
||||
|
||||
resultFilter.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.True(context.Canceled);
|
||||
Assert.Same(context.Result, result.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_ShortCircuit_WithResult()
|
||||
{
|
||||
// Arrange
|
||||
var result = new Mock<IActionResult>(MockBehavior.Strict);
|
||||
result
|
||||
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var actionFilter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => context = c)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter2 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>((c, next) =>
|
||||
{
|
||||
// Notice we're not calling next
|
||||
c.Result = result.Object;
|
||||
return Task.FromResult(true);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter3 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
|
||||
var resultFilter1 = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter1.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter1.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
var resultFilter2 = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter2.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter2.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
{
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
actionFilter3.Object,
|
||||
resultFilter1.Object,
|
||||
resultFilter2.Object,
|
||||
});
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
result.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
actionFilter2.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
resultFilter1.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter1.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
resultFilter2.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter2.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.True(context.Canceled);
|
||||
Assert.Same(context.Result, result.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_ShortCircuit_WithoutResult()
|
||||
{
|
||||
// Arrange
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var actionFilter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => context = c)
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter2 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>((c, next) =>
|
||||
{
|
||||
// Notice we're not calling next
|
||||
return Task.FromResult(true);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter3 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[]
|
||||
{
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
actionFilter3.Object,
|
||||
resultFilter.Object,
|
||||
});
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
actionFilter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
actionFilter2.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
resultFilter.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.True(context.Canceled);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_ShortCircuit_WithResult_CallNext()
|
||||
{
|
||||
// Arrange
|
||||
var actionFilter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
c.Result = new EmptyResult();
|
||||
await next();
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var message =
|
||||
"If an IAsyncActionFilter provides a result value by setting the Result property of " +
|
||||
"ActionExecutingContext to a non-null value, then it cannot call the next filter by invoking " +
|
||||
"ActionExecutionDelegate.";
|
||||
|
||||
var invoker = CreateInvoker(actionFilter.Object);
|
||||
|
||||
// Act & Assert
|
||||
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await invoker.InvokeAsync(),
|
||||
message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_WithExceptionThrownByAction()
|
||||
{
|
||||
// Arrange
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c =>
|
||||
{
|
||||
context = c;
|
||||
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(c.ExceptionHandled);
|
||||
c.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(filter.Object, exception: Exception);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
Assert.Same(Exception, context.Exception);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_WithExceptionThrownByActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var exception = new DataMisalignedException();
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var filter1 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter1.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter1
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c =>
|
||||
{
|
||||
context = c;
|
||||
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(c.ExceptionHandled);
|
||||
c.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var filter2 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter2
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>(c => { throw exception; })
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new[] { filter1.Object, filter2.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter1.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter1.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
filter2.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
filter2.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Never());
|
||||
|
||||
Assert.Same(exception, context.Exception);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncActionFilter_WithExceptionThrownByActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var exception = new DataMisalignedException();
|
||||
ActionExecutedContext context = null;
|
||||
|
||||
var filter1 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
filter1
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(context.ExceptionHandled);
|
||||
context.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var filter2 = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
filter2.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
filter2
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c => { throw exception; })
|
||||
.Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { filter1.Object, filter2.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
filter1.Verify(
|
||||
f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()),
|
||||
Times.Once());
|
||||
|
||||
filter2.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
|
||||
Assert.Same(exception, context.Exception);
|
||||
Assert.Null(context.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesActionFilter_HandleException()
|
||||
{
|
||||
// Arrange
|
||||
var result = new Mock<IActionResult>(MockBehavior.Strict);
|
||||
result
|
||||
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
||||
.Returns<ActionContext>((context) => Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
||||
.Callback<ActionExecutedContext>(c =>
|
||||
{
|
||||
// Handle the exception so the test doesn't throw.
|
||||
Assert.False(c.ExceptionHandled);
|
||||
c.ExceptionHandled = true;
|
||||
|
||||
c.Result = result.Object;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
||||
resultFilter.Setup(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>())).Verifiable();
|
||||
resultFilter.Setup(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>())).Verifiable();
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
new IFilterMetadata[] { actionFilter.Object, resultFilter.Object },
|
||||
exception: Exception);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
actionFilter.Verify(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()), Times.Once());
|
||||
actionFilter.Verify(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()), Times.Once());
|
||||
|
||||
resultFilter.Verify(f => f.OnResultExecuting(It.IsAny<ResultExecutingContext>()), Times.Once());
|
||||
resultFilter.Verify(f => f.OnResultExecuted(It.IsAny<ResultExecutedContext>()), Times.Once());
|
||||
|
||||
result.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesResultFilter()
|
||||
{
|
||||
|
|
@ -1653,43 +1216,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_WithActionResult_FromActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = Mock.Of<IActionResult>();
|
||||
|
||||
ResourceExecutedContext context = null;
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>((c) =>
|
||||
{
|
||||
c.Result = expected;
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { resourceFilter.Object, actionFilter.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, context.Result);
|
||||
|
||||
resourceFilter.Verify(
|
||||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_WithActionResult_FromExceptionFilter()
|
||||
{
|
||||
|
|
@ -1795,84 +1321,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_HandleException_FromActionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new DataMisalignedException();
|
||||
|
||||
ResourceExecutedContext context = null;
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
context.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
||||
actionFilter
|
||||
.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>()))
|
||||
.Callback<ActionExecutingContext>((c) =>
|
||||
{
|
||||
throw expected;
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { resourceFilter.Object, actionFilter.Object });
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, context.Exception);
|
||||
Assert.Same(expected, context.ExceptionDispatchInfo.SourceException);
|
||||
|
||||
resourceFilter.Verify(
|
||||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_HandlesException_FromExceptionFilter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new DataMisalignedException();
|
||||
|
||||
ResourceExecutedContext context = null;
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
context = await next();
|
||||
context.ExceptionHandled = true;
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var exceptionFilter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
||||
exceptionFilter
|
||||
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
||||
.Callback<ExceptionContext>((c) =>
|
||||
{
|
||||
throw expected;
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(new IFilterMetadata[] { resourceFilter.Object, exceptionFilter.Object }, exception: Exception);
|
||||
|
||||
// Act
|
||||
await invoker.InvokeAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, context.Exception);
|
||||
Assert.Same(expected, context.ExceptionDispatchInfo.SourceException);
|
||||
|
||||
resourceFilter.Verify(
|
||||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_InvokesAsyncResourceFilter_HandlesException_FromResultFilter()
|
||||
{
|
||||
|
|
@ -2082,7 +1530,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
var resourceFilter3 = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
var exceptionFilter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
||||
var actionFilter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
var resultFilter = new Mock<IAsyncResultFilter>(MockBehavior.Strict);
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
|
|
@ -2092,7 +1539,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
resourceFilter2.Object, // This filter will short circuit
|
||||
resourceFilter3.Object, // This shouldn't run - it will throw if it does
|
||||
exceptionFilter.Object, // This shouldn't run - it will throw if it does
|
||||
actionFilter.Object, // This shouldn't run - it will throw if it does
|
||||
resultFilter.Object // This shouldn't run - it will throw if it does
|
||||
},
|
||||
// The action won't run
|
||||
|
|
@ -2132,7 +1578,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
var resourceFilter3 = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
var exceptionFilter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
||||
var actionFilter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
var resultFilter = new Mock<IAsyncResultFilter>(MockBehavior.Strict);
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
|
|
@ -2142,7 +1587,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
resourceFilter2.Object, // This filter will short circuit
|
||||
resourceFilter3.Object, // This shouldn't run - it will throw if it does
|
||||
exceptionFilter.Object, // This shouldn't run - it will throw if it does
|
||||
actionFilter.Object, // This shouldn't run - it will throw if it does
|
||||
resultFilter.Object // This shouldn't run - it will throw if it does
|
||||
},
|
||||
// The action won't run
|
||||
|
|
@ -2184,7 +1628,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
});
|
||||
|
||||
var resourceFilter3 = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
var actionFilter = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
var resultFilter = new Mock<IAsyncResultFilter>(MockBehavior.Strict);
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
|
|
@ -2193,7 +1636,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
resourceFilter1.Object, // This filter should see the result retured from resourceFilter2
|
||||
resourceFilter2.Object,
|
||||
resourceFilter3.Object, // This shouldn't run - it will throw if it does
|
||||
actionFilter.Object, // This shouldn't run - it will throw if it does
|
||||
resultFilter.Object // This shouldn't run - it will throw if it does
|
||||
},
|
||||
// The action won't run
|
||||
|
|
@ -2268,49 +1710,5 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeAction_ExceptionBubbling_AsyncActionFilter_To_ResourceFilter()
|
||||
{
|
||||
// Arrange
|
||||
var resourceFilter = new Mock<IAsyncResourceFilter>(MockBehavior.Strict);
|
||||
resourceFilter
|
||||
.Setup(f => f.OnResourceExecutionAsync(It.IsAny<ResourceExecutingContext>(), It.IsAny<ResourceExecutionDelegate>()))
|
||||
.Returns<ResourceExecutingContext, ResourceExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
var context = await next();
|
||||
Assert.Same(Exception, context.Exception);
|
||||
context.ExceptionHandled = true;
|
||||
});
|
||||
|
||||
var actionFilter1 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter1
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
await next();
|
||||
});
|
||||
|
||||
var actionFilter2 = new Mock<IAsyncActionFilter>(MockBehavior.Strict);
|
||||
actionFilter2
|
||||
.Setup(f => f.OnActionExecutionAsync(It.IsAny<ActionExecutingContext>(), It.IsAny<ActionExecutionDelegate>()))
|
||||
.Returns<ActionExecutingContext, ActionExecutionDelegate>(async (c, next) =>
|
||||
{
|
||||
await next();
|
||||
});
|
||||
|
||||
var invoker = CreateInvoker(
|
||||
new IFilterMetadata[]
|
||||
{
|
||||
resourceFilter.Object,
|
||||
actionFilter1.Object,
|
||||
actionFilter2.Object,
|
||||
},
|
||||
// The action won't run
|
||||
exception: Exception);
|
||||
|
||||
// Act & Assert
|
||||
await invoker.InvokeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue