Merge invoker back into one class
This will help future generations maintain this class. Notice that the protected methods that are going away all just call into another extensibility point (other than the executor). If we need to we could make that extensible in the future and then we have the same support with fewer hooks and less complexity.
This commit is contained in:
parent
51e133ab9f
commit
1e02fd2e6b
|
|
@ -7,7 +7,9 @@ using System.Diagnostics;
|
|||
#if NETSTANDARD1_5
|
||||
using System.Reflection;
|
||||
#endif
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
|
@ -17,108 +19,758 @@ using Microsoft.Extensions.Logging;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class ControllerActionInvoker : FilterActionInvoker
|
||||
public class ControllerActionInvoker : IActionInvoker
|
||||
{
|
||||
private readonly ControllerActionDescriptor _descriptor;
|
||||
private readonly IControllerFactory _controllerFactory;
|
||||
private readonly IControllerArgumentBinder _argumentBinder;
|
||||
private readonly IControllerArgumentBinder _controllerArgumentBinder;
|
||||
private readonly DiagnosticSource _diagnosticSource;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly ControllerContext _controllerContext;
|
||||
private readonly IFilterMetadata[] _filters;
|
||||
private readonly ObjectMethodExecutor _executor;
|
||||
|
||||
// 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/
|
||||
private FilterCursor _cursor;
|
||||
private object _controller;
|
||||
|
||||
private AuthorizationFilterContext _authorizationContext;
|
||||
|
||||
private ResourceExecutingContext _resourceExecutingContext;
|
||||
private ResourceExecutedContext _resourceExecutedContext;
|
||||
|
||||
private ExceptionContext _exceptionContext;
|
||||
|
||||
private ActionExecutingContext _actionExecutingContext;
|
||||
private ActionExecutedContext _actionExecutedContext;
|
||||
|
||||
private ResultExecutingContext _resultExecutingContext;
|
||||
private ResultExecutedContext _resultExecutedContext;
|
||||
|
||||
public ControllerActionInvoker(
|
||||
ActionContext actionContext,
|
||||
ControllerActionInvokerCache controllerActionInvokerCache,
|
||||
ControllerActionInvokerCache cache,
|
||||
IControllerFactory controllerFactory,
|
||||
ControllerActionDescriptor descriptor,
|
||||
IControllerArgumentBinder argumentBinder,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
IControllerArgumentBinder controllerArgumentBinder,
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
ActionContext actionContext,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
int maxModelValidationErrors)
|
||||
: base(
|
||||
actionContext,
|
||||
controllerActionInvokerCache,
|
||||
valueProviderFactories,
|
||||
logger,
|
||||
diagnosticSource,
|
||||
maxModelValidationErrors)
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cache));
|
||||
}
|
||||
|
||||
if (controllerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(controllerFactory));
|
||||
}
|
||||
|
||||
if (descriptor == null)
|
||||
if (controllerArgumentBinder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(descriptor));
|
||||
throw new ArgumentNullException(nameof(controllerArgumentBinder));
|
||||
}
|
||||
|
||||
if (argumentBinder == null)
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(argumentBinder));
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
if (diagnosticSource == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(diagnosticSource));
|
||||
}
|
||||
|
||||
if (actionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionContext));
|
||||
}
|
||||
|
||||
if (valueProviderFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueProviderFactories));
|
||||
}
|
||||
|
||||
_controllerFactory = controllerFactory;
|
||||
_descriptor = descriptor;
|
||||
_argumentBinder = argumentBinder;
|
||||
_controllerArgumentBinder = controllerArgumentBinder;
|
||||
_logger = logger;
|
||||
_diagnosticSource = diagnosticSource;
|
||||
|
||||
if (descriptor.MethodInfo == null)
|
||||
_controllerContext = new ControllerContext(actionContext);
|
||||
_controllerContext.ModelState.MaxAllowedErrors = maxModelValidationErrors;
|
||||
|
||||
// PERF: These are rarely going to be changed, so let's go copy-on-write.
|
||||
_controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(valueProviderFactories);
|
||||
|
||||
var cacheEntry = cache.GetState(_controllerContext);
|
||||
_filters = cacheEntry.Filters;
|
||||
_executor = cacheEntry.ActionMethodExecutor;
|
||||
_cursor = new FilterCursor(_filters);
|
||||
}
|
||||
|
||||
public virtual async Task InvokeAsync()
|
||||
{
|
||||
await InvokeAllAuthorizationFiltersAsync();
|
||||
|
||||
// If Authorization Filters return a result, it's a short circuit because
|
||||
// authorization failed. We don't execute Result Filters around the result.
|
||||
Debug.Assert(_authorizationContext != null);
|
||||
if (_authorizationContext.Result != null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatPropertyOfTypeCannotBeNull(
|
||||
nameof(descriptor.MethodInfo),
|
||||
typeof(ControllerActionDescriptor)),
|
||||
nameof(descriptor));
|
||||
await InvokeResultAsync(_authorizationContext.Result);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAllResourceFiltersAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Release the instance after all filters have run. We don't need to surround
|
||||
// Authorizations filters because the instance will be created much later than
|
||||
// that.
|
||||
if (_controller != null)
|
||||
{
|
||||
_controllerFactory.ReleaseController(_controllerContext, _controller);
|
||||
}
|
||||
}
|
||||
|
||||
// We've reached the end of resource filters. If there's an unhandled exception on the context then
|
||||
// it should be thrown and middleware has a chance to handle it.
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
if (_resourceExecutedContext.Exception != null && !_resourceExecutedContext.ExceptionHandled)
|
||||
{
|
||||
if (_resourceExecutedContext.ExceptionDispatchInfo == null)
|
||||
{
|
||||
throw _resourceExecutedContext.Exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
_resourceExecutedContext.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override object CreateInstance()
|
||||
private Task InvokeAllAuthorizationFiltersAsync()
|
||||
{
|
||||
return _controllerFactory.CreateController(Context);
|
||||
_cursor.Reset();
|
||||
|
||||
_authorizationContext = new AuthorizationFilterContext(_controllerContext, _filters);
|
||||
return InvokeAuthorizationFilterAsync();
|
||||
}
|
||||
|
||||
protected override void ReleaseInstance(object instance)
|
||||
private async Task InvokeAuthorizationFilterAsync()
|
||||
{
|
||||
_controllerFactory.ReleaseController(Context, instance);
|
||||
}
|
||||
// We should never get here if we already have a result.
|
||||
Debug.Assert(_authorizationContext != null);
|
||||
Debug.Assert(_authorizationContext.Result == null);
|
||||
|
||||
protected override async Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext)
|
||||
{
|
||||
if (actionExecutingContext == null)
|
||||
var current = _cursor.GetNextFilter<IAuthorizationFilter, IAsyncAuthorizationFilter>();
|
||||
if (current.FilterAsync != null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionExecutingContext));
|
||||
_diagnosticSource.BeforeOnAuthorizationAsync(_authorizationContext, current.FilterAsync);
|
||||
|
||||
await current.FilterAsync.OnAuthorizationAsync(_authorizationContext);
|
||||
|
||||
_diagnosticSource.AfterOnAuthorizationAsync(_authorizationContext, current.FilterAsync);
|
||||
|
||||
if (_authorizationContext.Result == null)
|
||||
{
|
||||
// Only keep going if we don't have a result
|
||||
await InvokeAuthorizationFilterAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.AuthorizationFailure(current.FilterAsync);
|
||||
}
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnAuthorization(_authorizationContext, current.Filter);
|
||||
|
||||
current.Filter.OnAuthorization(_authorizationContext);
|
||||
|
||||
_diagnosticSource.AfterOnAuthorization(_authorizationContext, current.Filter);
|
||||
|
||||
if (_authorizationContext.Result == null)
|
||||
{
|
||||
// Only keep going if we don't have a result
|
||||
await InvokeAuthorizationFilterAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.AuthorizationFailure(current.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've run out of Authorization Filters - if we haven't short circuited by now then this
|
||||
// request is authorized.
|
||||
}
|
||||
}
|
||||
|
||||
private Task InvokeAllResourceFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_resourceExecutingContext = new ResourceExecutingContext(_controllerContext, _filters);
|
||||
return InvokeResourceFilterAsync();
|
||||
}
|
||||
|
||||
private async Task<ResourceExecutedContext> InvokeResourceFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_resourceExecutingContext != null);
|
||||
|
||||
if (_resourceExecutingContext.Result != null)
|
||||
{
|
||||
// If we get here, it means that an async filter set a result AND called next(). This is forbidden.
|
||||
var message = Resources.FormatAsyncResourceFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncResourceFilter).Name,
|
||||
nameof(ResourceExecutingContext.Result),
|
||||
typeof(ResourceExecutingContext).Name,
|
||||
typeof(ResourceExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var actionMethodInfo = _descriptor.MethodInfo;
|
||||
await InvokeResourceFilterAsync();
|
||||
|
||||
var methodExecutor = GetControllerActionMethodExecutor();
|
||||
|
||||
var arguments = ControllerActionExecutor.PrepareArguments(
|
||||
actionExecutingContext.ActionArguments,
|
||||
actionMethodInfo.GetParameters());
|
||||
|
||||
Logger.ActionMethodExecuting(actionExecutingContext, arguments);
|
||||
|
||||
var actionReturnValue = await ControllerActionExecutor.ExecuteAsync(
|
||||
methodExecutor,
|
||||
actionExecutingContext.Controller,
|
||||
arguments);
|
||||
|
||||
var actionResult = CreateActionResult(
|
||||
actionMethodInfo.ReturnType,
|
||||
actionReturnValue);
|
||||
|
||||
Logger.ActionMethodExecuted(actionExecutingContext, actionResult);
|
||||
|
||||
return actionResult;
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
return _resourceExecutedContext;
|
||||
}
|
||||
|
||||
protected override Task BindActionArgumentsAsync(IDictionary<string, object> arguments)
|
||||
private async Task InvokeResourceFilterAsync()
|
||||
{
|
||||
if (arguments == null)
|
||||
Debug.Assert(_resourceExecutingContext != null);
|
||||
|
||||
var item = _cursor.GetNextFilter<IResourceFilter, IAsyncResourceFilter>();
|
||||
try
|
||||
{
|
||||
throw new ArgumentNullException(nameof(arguments));
|
||||
if (item.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResourceExecution(_resourceExecutingContext, item.FilterAsync);
|
||||
|
||||
await item.FilterAsync.OnResourceExecutionAsync(_resourceExecutingContext, InvokeResourceFilterAwaitedAsync);
|
||||
|
||||
if (_resourceExecutedContext == null)
|
||||
{
|
||||
// If we get here then the filter didn't call 'next' indicating a short circuit
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _resourceExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnResourceExecution(_resourceExecutedContext, item.FilterAsync);
|
||||
|
||||
if (_resourceExecutingContext.Result != null)
|
||||
{
|
||||
_logger.ResourceFilterShortCircuited(item.FilterAsync);
|
||||
|
||||
await InvokeResultAsync(_resourceExecutingContext.Result);
|
||||
}
|
||||
}
|
||||
else if (item.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResourceExecuting(_resourceExecutingContext, item.Filter);
|
||||
|
||||
item.Filter.OnResourceExecuting(_resourceExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnResourceExecuting(_resourceExecutingContext, item.Filter);
|
||||
|
||||
if (_resourceExecutingContext.Result != null)
|
||||
{
|
||||
// Short-circuited by setting a result.
|
||||
_logger.ResourceFilterShortCircuited(item.Filter);
|
||||
|
||||
await InvokeResultAsync(_resourceExecutingContext.Result);
|
||||
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _resourceExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeResourceFilterAsync();
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
|
||||
_diagnosticSource.BeforeOnResourceExecuted(_resourceExecutedContext, item.Filter);
|
||||
|
||||
item.Filter.OnResourceExecuted(_resourceExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnResourceExecuted(_resourceExecutedContext, item.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// >> ExceptionFilters >> Model Binding >> ActionFilters >> Action
|
||||
await InvokeAllExceptionFiltersAsync();
|
||||
|
||||
// If Exception Filters provide a result, it's a short-circuit due to an exception.
|
||||
// We don't execute Result Filters around the result.
|
||||
Debug.Assert(_exceptionContext != null);
|
||||
if (_exceptionContext.Result != null)
|
||||
{
|
||||
// This means that exception filters returned a result to 'handle' an error.
|
||||
// We're not interested in seeing the exception details since it was handled.
|
||||
await InvokeResultAsync(_exceptionContext.Result);
|
||||
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Result = _exceptionContext.Result,
|
||||
};
|
||||
}
|
||||
else if (_exceptionContext.Exception != null)
|
||||
{
|
||||
// If we get here, this means that we have an unhandled exception.
|
||||
// Exception filted didn't handle this, so send it on to resource filters.
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters);
|
||||
|
||||
// Preserve the stack trace if possible.
|
||||
_resourceExecutedContext.Exception = _exceptionContext.Exception;
|
||||
if (_exceptionContext.ExceptionDispatchInfo != null)
|
||||
{
|
||||
_resourceExecutedContext.ExceptionDispatchInfo = _exceptionContext.ExceptionDispatchInfo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have a successful 'result' from the action or an Action Filter, so run
|
||||
// Result Filters.
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
var result = _actionExecutedContext.Result;
|
||||
|
||||
// >> ResultFilters >> (Result)
|
||||
await InvokeAllResultFiltersAsync(result);
|
||||
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Result = _resultExecutedContext.Result,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
|
||||
};
|
||||
}
|
||||
|
||||
return _argumentBinder.BindArgumentsAsync(Context, Instance, arguments);
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
}
|
||||
|
||||
private Task InvokeAllExceptionFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
return InvokeExceptionFilterAsync();
|
||||
}
|
||||
|
||||
private async Task InvokeExceptionFilterAsync()
|
||||
{
|
||||
var current = _cursor.GetNextFilter<IExceptionFilter, IAsyncExceptionFilter>();
|
||||
if (current.FilterAsync != null)
|
||||
{
|
||||
// Exception filters run "on the way out" - so the filter is run after the rest of the
|
||||
// pipeline.
|
||||
await InvokeExceptionFilterAsync();
|
||||
|
||||
Debug.Assert(_exceptionContext != null);
|
||||
if (_exceptionContext.Exception != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnExceptionAsync(_exceptionContext, current.FilterAsync);
|
||||
|
||||
// Exception filters only run when there's an exception - unsetting it will short-circuit
|
||||
// other exception filters.
|
||||
await current.FilterAsync.OnExceptionAsync(_exceptionContext);
|
||||
|
||||
_diagnosticSource.AfterOnExceptionAsync(_exceptionContext, current.FilterAsync);
|
||||
|
||||
if (_exceptionContext.Exception == null)
|
||||
{
|
||||
_logger.ExceptionFilterShortCircuited(current.FilterAsync);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
// Exception filters run "on the way out" - so the filter is run after the rest of the
|
||||
// pipeline.
|
||||
await InvokeExceptionFilterAsync();
|
||||
|
||||
Debug.Assert(_exceptionContext != null);
|
||||
if (_exceptionContext.Exception != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnException(_exceptionContext, current.Filter);
|
||||
|
||||
// Exception filters only run when there's an exception - unsetting it will short-circuit
|
||||
// other exception filters.
|
||||
current.Filter.OnException(_exceptionContext);
|
||||
|
||||
_diagnosticSource.AfterOnException(_exceptionContext, current.Filter);
|
||||
|
||||
if (_exceptionContext.Exception == null)
|
||||
{
|
||||
_logger.ExceptionFilterShortCircuited(current.Filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've reached the 'end' of the exception filter pipeline - this means that one stack frame has
|
||||
// been built for each exception. When we return from here, these frames will either:
|
||||
//
|
||||
// 1) Call the filter (if we have an exception)
|
||||
// 2) No-op (if we don't have an exception)
|
||||
Debug.Assert(_exceptionContext == null);
|
||||
_exceptionContext = new ExceptionContext(_controllerContext, _filters);
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAllActionFiltersAsync();
|
||||
|
||||
// Action filters might 'return' an unhandled exception instead of throwing
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
if (_actionExecutedContext.Exception != null && !_actionExecutedContext.ExceptionHandled)
|
||||
{
|
||||
_exceptionContext.Exception = _actionExecutedContext.Exception;
|
||||
if (_actionExecutedContext.ExceptionDispatchInfo != null)
|
||||
{
|
||||
_exceptionContext.ExceptionDispatchInfo = _actionExecutedContext.ExceptionDispatchInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_exceptionContext.ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InvokeAllActionFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_controller = _controllerFactory.CreateController(_controllerContext);
|
||||
|
||||
var arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
await _controllerArgumentBinder.BindArgumentsAsync(_controllerContext, _controller, arguments);
|
||||
_actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, arguments, _controller);
|
||||
|
||||
await InvokeActionFilterAsync();
|
||||
}
|
||||
|
||||
private async Task<ActionExecutedContext> InvokeActionFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_actionExecutingContext != null);
|
||||
if (_actionExecutingContext.Result != null)
|
||||
{
|
||||
// If we get here, it means that an async filter set a result AND called next(). This is forbidden.
|
||||
var message = Resources.FormatAsyncActionFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncActionFilter).Name,
|
||||
nameof(ActionExecutingContext.Result),
|
||||
typeof(ActionExecutingContext).Name,
|
||||
typeof(ActionExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
await InvokeActionFilterAsync();
|
||||
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
return _actionExecutedContext;
|
||||
}
|
||||
|
||||
private async Task InvokeActionFilterAsync()
|
||||
{
|
||||
Debug.Assert(_actionExecutingContext != null);
|
||||
|
||||
var item = _cursor.GetNextFilter<IActionFilter, IAsyncActionFilter>();
|
||||
try
|
||||
{
|
||||
if (item.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnActionExecution(_actionExecutingContext, item.FilterAsync);
|
||||
|
||||
await item.FilterAsync.OnActionExecutionAsync(_actionExecutingContext, InvokeActionFilterAwaitedAsync);
|
||||
|
||||
if (_actionExecutedContext == null)
|
||||
{
|
||||
// If we get here then the filter didn't call 'next' indicating a short circuit
|
||||
_logger.ActionFilterShortCircuited(item.FilterAsync);
|
||||
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
_controller)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _actionExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnActionExecution(_actionExecutedContext, item.FilterAsync);
|
||||
}
|
||||
else if (item.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnActionExecuting(_actionExecutingContext, item.Filter);
|
||||
|
||||
item.Filter.OnActionExecuting(_actionExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnActionExecuting(_actionExecutingContext, item.Filter);
|
||||
|
||||
if (_actionExecutingContext.Result != null)
|
||||
{
|
||||
// Short-circuited by setting a result.
|
||||
_logger.ActionFilterShortCircuited(item.Filter);
|
||||
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
_controller)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _actionExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeActionFilterAsync();
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
|
||||
_diagnosticSource.BeforeOnActionExecuted(_actionExecutedContext, item.Filter);
|
||||
|
||||
item.Filter.OnActionExecuted(_actionExecutedContext);
|
||||
|
||||
_diagnosticSource.BeforeOnActionExecuted(_actionExecutedContext, item.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All action filters have run, execute the action method.
|
||||
IActionResult result = null;
|
||||
|
||||
try
|
||||
{
|
||||
_diagnosticSource.BeforeActionMethod(
|
||||
_controllerContext,
|
||||
_actionExecutingContext.ActionArguments,
|
||||
_actionExecutingContext.Controller);
|
||||
|
||||
var actionMethodInfo = _controllerContext.ActionDescriptor.MethodInfo;
|
||||
|
||||
var arguments = ControllerActionExecutor.PrepareArguments(
|
||||
_actionExecutingContext.ActionArguments,
|
||||
actionMethodInfo.GetParameters());
|
||||
|
||||
_logger.ActionMethodExecuting(_actionExecutingContext, arguments);
|
||||
|
||||
var actionReturnValue = await ControllerActionExecutor.ExecuteAsync(
|
||||
_executor,
|
||||
_controller,
|
||||
arguments);
|
||||
|
||||
result = CreateActionResult( actionMethodInfo.ReturnType, actionReturnValue);
|
||||
|
||||
_logger.ActionMethodExecuted(_actionExecutingContext, result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_diagnosticSource.AfterActionMethod(
|
||||
_controllerContext,
|
||||
_actionExecutingContext.ActionArguments,
|
||||
_actionExecutingContext.Controller,
|
||||
result);
|
||||
}
|
||||
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
_controller)
|
||||
{
|
||||
Result = result
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// Exceptions thrown by the action method OR filters bubble back up through ActionExcecutedContext.
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
_controller)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task InvokeAllResultFiltersAsync(IActionResult result)
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_resultExecutingContext = new ResultExecutingContext(_controllerContext, _filters, result, _controller);
|
||||
await InvokeResultFilterAsync();
|
||||
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
if (_resultExecutedContext.Exception != null && !_resultExecutedContext.ExceptionHandled)
|
||||
{
|
||||
// There's an unhandled exception in filters
|
||||
if (_resultExecutedContext.ExceptionDispatchInfo != null)
|
||||
{
|
||||
_resultExecutedContext.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw _resultExecutedContext.Exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ResultExecutedContext> InvokeResultFilterAwaitedAsync()
|
||||
{
|
||||
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 InvokeResultFilterAsync();
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
return _resultExecutedContext;
|
||||
}
|
||||
|
||||
private async Task InvokeResultFilterAsync()
|
||||
{
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
|
||||
try
|
||||
{
|
||||
var item = _cursor.GetNextFilter<IResultFilter, IAsyncResultFilter>();
|
||||
if (item.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResultExecution(_resultExecutingContext, item.FilterAsync);
|
||||
|
||||
await item.FilterAsync.OnResultExecutionAsync(_resultExecutingContext, InvokeResultFilterAwaitedAsync);
|
||||
|
||||
if (_resultExecutedContext == null || _resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// Short-circuited by not calling next || Short-circuited by setting Cancel == true
|
||||
_logger.ResourceFilterShortCircuited(item.FilterAsync);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
_controller)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnResultExecution(_resultExecutedContext, item.FilterAsync);
|
||||
}
|
||||
else if (item.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResultExecuting(_resultExecutingContext, item.Filter);
|
||||
|
||||
item.Filter.OnResultExecuting(_resultExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuting(_resultExecutingContext, item.Filter);
|
||||
|
||||
if (_resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// Short-circuited by setting Cancel == true
|
||||
_logger.ResourceFilterShortCircuited(item.Filter);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
_controller)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeResultFilterAsync();
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecuted(_resultExecutedContext, item.Filter);
|
||||
|
||||
item.Filter.OnResultExecuted(_resultExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuted(_resultExecutedContext, item.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
// The empty result is always flowed back as the 'executed' result
|
||||
if (_resultExecutingContext.Result == null)
|
||||
{
|
||||
_resultExecutingContext.Result = new EmptyResult();
|
||||
}
|
||||
|
||||
await InvokeResultAsync(_resultExecutingContext.Result);
|
||||
|
||||
Debug.Assert(_resultExecutedContext == null);
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
_controller);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
_controller)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task InvokeResultAsync(IActionResult result)
|
||||
{
|
||||
_diagnosticSource.BeforeActionResult(_controllerContext, result);
|
||||
|
||||
try
|
||||
{
|
||||
await result.ExecuteResultAsync(_controllerContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_diagnosticSource.AfterActionResult(_controllerContext, result);
|
||||
}
|
||||
}
|
||||
|
||||
// Marking as internal for Unit Testing purposes.
|
||||
|
|
@ -163,5 +815,82 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
return genericType?.GenericTypeArguments[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A one-way cursor for filters.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will iterate the filter collection once per-stage, and skip any filters that don't have
|
||||
/// the one of interfaces that applies to the current stage.
|
||||
///
|
||||
/// Filters are always executed in the following order, but short circuiting plays a role.
|
||||
///
|
||||
/// Indentation reflects nesting.
|
||||
///
|
||||
/// 1. Exception Filters
|
||||
/// 2. Authorization Filters
|
||||
/// 3. Action Filters
|
||||
/// Action
|
||||
///
|
||||
/// 4. Result Filters
|
||||
/// Result
|
||||
///
|
||||
/// </remarks>
|
||||
private struct FilterCursor
|
||||
{
|
||||
private int _index;
|
||||
private readonly IFilterMetadata[] _filters;
|
||||
|
||||
public FilterCursor(int index, IFilterMetadata[] filters)
|
||||
{
|
||||
_index = index;
|
||||
_filters = filters;
|
||||
}
|
||||
|
||||
public FilterCursor(IFilterMetadata[] filters)
|
||||
{
|
||||
_index = 0;
|
||||
_filters = filters;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
public FilterCursorItem<TFilter, TFilterAsync> GetNextFilter<TFilter, TFilterAsync>()
|
||||
where TFilter : class
|
||||
where TFilterAsync : class
|
||||
{
|
||||
while (_index < _filters.Length)
|
||||
{
|
||||
var filter = _filters[_index] as TFilter;
|
||||
var filterAsync = _filters[_index] as TFilterAsync;
|
||||
|
||||
_index += 1;
|
||||
|
||||
if (filter != null || filterAsync != null)
|
||||
{
|
||||
return new FilterCursorItem<TFilter, TFilterAsync>(_index, filter, filterAsync);
|
||||
}
|
||||
}
|
||||
|
||||
return default(FilterCursorItem<TFilter, TFilterAsync>);
|
||||
}
|
||||
}
|
||||
|
||||
private struct FilterCursorItem<TFilter, TFilterAsync>
|
||||
{
|
||||
public readonly int Index;
|
||||
public readonly TFilter Filter;
|
||||
public readonly TFilterAsync FilterAsync;
|
||||
|
||||
public FilterCursorItem(int index, TFilter filter, TFilterAsync filterAsync)
|
||||
{
|
||||
Index = index;
|
||||
Filter = filter;
|
||||
FilterAsync = filterAsync;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,14 +58,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
if (actionDescriptor != null)
|
||||
{
|
||||
context.Result = new ControllerActionInvoker(
|
||||
context.ActionContext,
|
||||
_controllerActionInvokerCache,
|
||||
_controllerFactory,
|
||||
actionDescriptor,
|
||||
_argumentBinder,
|
||||
_valueProviderFactories,
|
||||
_logger,
|
||||
_diagnosticSource,
|
||||
context.ActionContext,
|
||||
_valueProviderFactories,
|
||||
_maxModelValidationErrors);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,845 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public abstract class FilterActionInvoker : IActionInvoker
|
||||
{
|
||||
private readonly ControllerActionInvokerCache _controllerActionInvokerCache;
|
||||
|
||||
private readonly DiagnosticSource _diagnosticSource;
|
||||
private readonly int _maxModelValidationErrors;
|
||||
|
||||
private IFilterMetadata[] _filters;
|
||||
private ObjectMethodExecutor _controllerActionMethodExecutor;
|
||||
private FilterCursor _cursor;
|
||||
|
||||
private AuthorizationFilterContext _authorizationContext;
|
||||
|
||||
private ResourceExecutingContext _resourceExecutingContext;
|
||||
private ResourceExecutedContext _resourceExecutedContext;
|
||||
|
||||
private ExceptionContext _exceptionContext;
|
||||
|
||||
private ActionExecutingContext _actionExecutingContext;
|
||||
private ActionExecutedContext _actionExecutedContext;
|
||||
|
||||
private ResultExecutingContext _resultExecutingContext;
|
||||
private ResultExecutedContext _resultExecutedContext;
|
||||
|
||||
public FilterActionInvoker(
|
||||
ActionContext actionContext,
|
||||
ControllerActionInvokerCache controllerActionInvokerCache,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
int maxModelValidationErrors)
|
||||
{
|
||||
if (actionContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actionContext));
|
||||
}
|
||||
|
||||
if (controllerActionInvokerCache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(controllerActionInvokerCache));
|
||||
}
|
||||
|
||||
if (valueProviderFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueProviderFactories));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
if (diagnosticSource == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(diagnosticSource));
|
||||
}
|
||||
|
||||
|
||||
_controllerActionInvokerCache = controllerActionInvokerCache;
|
||||
Logger = logger;
|
||||
_diagnosticSource = diagnosticSource;
|
||||
_maxModelValidationErrors = maxModelValidationErrors;
|
||||
|
||||
Context = new ControllerContext(actionContext);
|
||||
Context.ModelState.MaxAllowedErrors = _maxModelValidationErrors;
|
||||
|
||||
// PERF: These are rarely going to be changed, so let's go copy-on-write.
|
||||
Context.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(valueProviderFactories);
|
||||
}
|
||||
|
||||
protected ControllerContext Context { get; }
|
||||
|
||||
protected object Instance { get; private set; }
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called to create an instance of an object which will act as the reciever of the action invocation.
|
||||
/// </summary>
|
||||
/// <returns>The constructed instance or <c>null</c>.</returns>
|
||||
protected abstract object CreateInstance();
|
||||
|
||||
/// <summary>
|
||||
/// Called to create an instance of an object which will act as the reciever of the action invocation.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance to release.</param>
|
||||
/// <remarks>This method will not be called if <see cref="CreateInstance"/> returns <c>null</c>.</remarks>
|
||||
protected abstract void ReleaseInstance(object instance);
|
||||
|
||||
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
|
||||
|
||||
protected abstract Task BindActionArgumentsAsync(IDictionary<string, object> arguments);
|
||||
|
||||
public virtual async Task InvokeAsync()
|
||||
{
|
||||
var controllerActionInvokerState = _controllerActionInvokerCache.GetState(Context);
|
||||
_filters = controllerActionInvokerState.Filters;
|
||||
_controllerActionMethodExecutor = controllerActionInvokerState.ActionMethodExecutor;
|
||||
_cursor = new FilterCursor(_filters);
|
||||
|
||||
await InvokeAllAuthorizationFiltersAsync();
|
||||
|
||||
// If Authorization Filters return a result, it's a short circuit because
|
||||
// authorization failed. We don't execute Result Filters around the result.
|
||||
Debug.Assert(_authorizationContext != null);
|
||||
if (_authorizationContext.Result != null)
|
||||
{
|
||||
await InvokeResultAsync(_authorizationContext.Result);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAllResourceFiltersAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Release the instance after all filters have run. We don't need to surround
|
||||
// Authorizations filters because the instance will be created much later than
|
||||
// that.
|
||||
if (Instance != null)
|
||||
{
|
||||
ReleaseInstance(Instance);
|
||||
}
|
||||
}
|
||||
|
||||
// We've reached the end of resource filters. If there's an unhandled exception on the context then
|
||||
// it should be thrown and middleware has a chance to handle it.
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
if (_resourceExecutedContext.Exception != null && !_resourceExecutedContext.ExceptionHandled)
|
||||
{
|
||||
if (_resourceExecutedContext.ExceptionDispatchInfo == null)
|
||||
{
|
||||
throw _resourceExecutedContext.Exception;
|
||||
}
|
||||
else
|
||||
{
|
||||
_resourceExecutedContext.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ObjectMethodExecutor GetControllerActionMethodExecutor()
|
||||
{
|
||||
return _controllerActionMethodExecutor;
|
||||
}
|
||||
|
||||
private Task InvokeAllAuthorizationFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_authorizationContext = new AuthorizationFilterContext(Context, _filters);
|
||||
return InvokeAuthorizationFilterAsync();
|
||||
}
|
||||
|
||||
private async Task InvokeAuthorizationFilterAsync()
|
||||
{
|
||||
// We should never get here if we already have a result.
|
||||
Debug.Assert(_authorizationContext != null);
|
||||
Debug.Assert(_authorizationContext.Result == null);
|
||||
|
||||
var current = _cursor.GetNextFilter<IAuthorizationFilter, IAsyncAuthorizationFilter>();
|
||||
if (current.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnAuthorizationAsync(_authorizationContext, current.FilterAsync);
|
||||
|
||||
await current.FilterAsync.OnAuthorizationAsync(_authorizationContext);
|
||||
|
||||
_diagnosticSource.AfterOnAuthorizationAsync(_authorizationContext, current.FilterAsync);
|
||||
|
||||
if (_authorizationContext.Result == null)
|
||||
{
|
||||
// Only keep going if we don't have a result
|
||||
await InvokeAuthorizationFilterAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.AuthorizationFailure(current.FilterAsync);
|
||||
}
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnAuthorization(_authorizationContext, current.Filter);
|
||||
|
||||
current.Filter.OnAuthorization(_authorizationContext);
|
||||
|
||||
_diagnosticSource.AfterOnAuthorization(_authorizationContext, current.Filter);
|
||||
|
||||
if (_authorizationContext.Result == null)
|
||||
{
|
||||
// Only keep going if we don't have a result
|
||||
await InvokeAuthorizationFilterAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.AuthorizationFailure(current.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've run out of Authorization Filters - if we haven't short circuited by now then this
|
||||
// request is authorized.
|
||||
}
|
||||
}
|
||||
|
||||
private Task InvokeAllResourceFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_resourceExecutingContext = new ResourceExecutingContext(Context, _filters);
|
||||
return InvokeResourceFilterAsync();
|
||||
}
|
||||
|
||||
private async Task<ResourceExecutedContext> InvokeResourceFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_resourceExecutingContext != null);
|
||||
|
||||
if (_resourceExecutingContext.Result != null)
|
||||
{
|
||||
// If we get here, it means that an async filter set a result AND called next(). This is forbidden.
|
||||
var message = Resources.FormatAsyncResourceFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncResourceFilter).Name,
|
||||
nameof(ResourceExecutingContext.Result),
|
||||
typeof(ResourceExecutingContext).Name,
|
||||
typeof(ResourceExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
await InvokeResourceFilterAsync();
|
||||
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
return _resourceExecutedContext;
|
||||
}
|
||||
|
||||
private async Task InvokeResourceFilterAsync()
|
||||
{
|
||||
Debug.Assert(_resourceExecutingContext != null);
|
||||
|
||||
var item = _cursor.GetNextFilter<IResourceFilter, IAsyncResourceFilter>();
|
||||
try
|
||||
{
|
||||
if (item.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResourceExecution(_resourceExecutingContext, item.FilterAsync);
|
||||
|
||||
await item.FilterAsync.OnResourceExecutionAsync(_resourceExecutingContext, InvokeResourceFilterAwaitedAsync);
|
||||
|
||||
if (_resourceExecutedContext == null)
|
||||
{
|
||||
// If we get here then the filter didn't call 'next' indicating a short circuit
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _resourceExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnResourceExecution(_resourceExecutedContext, item.FilterAsync);
|
||||
|
||||
if (_resourceExecutingContext.Result != null)
|
||||
{
|
||||
Logger.ResourceFilterShortCircuited(item.FilterAsync);
|
||||
|
||||
await InvokeResultAsync(_resourceExecutingContext.Result);
|
||||
}
|
||||
}
|
||||
else if (item.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResourceExecuting(_resourceExecutingContext, item.Filter);
|
||||
|
||||
item.Filter.OnResourceExecuting(_resourceExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnResourceExecuting(_resourceExecutingContext, item.Filter);
|
||||
|
||||
if (_resourceExecutingContext.Result != null)
|
||||
{
|
||||
// Short-circuited by setting a result.
|
||||
Logger.ResourceFilterShortCircuited(item.Filter);
|
||||
|
||||
await InvokeResultAsync(_resourceExecutingContext.Result);
|
||||
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _resourceExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeResourceFilterAsync();
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
|
||||
_diagnosticSource.BeforeOnResourceExecuted(_resourceExecutedContext, item.Filter);
|
||||
|
||||
item.Filter.OnResourceExecuted(_resourceExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnResourceExecuted(_resourceExecutedContext, item.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// >> ExceptionFilters >> Model Binding >> ActionFilters >> Action
|
||||
await InvokeAllExceptionFiltersAsync();
|
||||
|
||||
// If Exception Filters provide a result, it's a short-circuit due to an exception.
|
||||
// We don't execute Result Filters around the result.
|
||||
Debug.Assert(_exceptionContext != null);
|
||||
if (_exceptionContext.Result != null)
|
||||
{
|
||||
// This means that exception filters returned a result to 'handle' an error.
|
||||
// We're not interested in seeing the exception details since it was handled.
|
||||
await InvokeResultAsync(_exceptionContext.Result);
|
||||
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Result = _exceptionContext.Result,
|
||||
};
|
||||
}
|
||||
else if (_exceptionContext.Exception != null)
|
||||
{
|
||||
// If we get here, this means that we have an unhandled exception.
|
||||
// Exception filted didn't handle this, so send it on to resource filters.
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters);
|
||||
|
||||
// Preserve the stack trace if possible.
|
||||
_resourceExecutedContext.Exception = _exceptionContext.Exception;
|
||||
if (_exceptionContext.ExceptionDispatchInfo != null)
|
||||
{
|
||||
_resourceExecutedContext.ExceptionDispatchInfo = _exceptionContext.ExceptionDispatchInfo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have a successful 'result' from the action or an Action Filter, so run
|
||||
// Result Filters.
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
var result = _actionExecutedContext.Result;
|
||||
|
||||
// >> ResultFilters >> (Result)
|
||||
await InvokeAllResultFiltersAsync(result);
|
||||
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
Result = _resultExecutedContext.Result,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_resourceExecutedContext != null);
|
||||
}
|
||||
|
||||
private Task InvokeAllExceptionFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
return InvokeExceptionFilterAsync();
|
||||
}
|
||||
|
||||
private async Task InvokeExceptionFilterAsync()
|
||||
{
|
||||
var current = _cursor.GetNextFilter<IExceptionFilter, IAsyncExceptionFilter>();
|
||||
if (current.FilterAsync != null)
|
||||
{
|
||||
// Exception filters run "on the way out" - so the filter is run after the rest of the
|
||||
// pipeline.
|
||||
await InvokeExceptionFilterAsync();
|
||||
|
||||
Debug.Assert(_exceptionContext != null);
|
||||
if (_exceptionContext.Exception != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnExceptionAsync(_exceptionContext, current.FilterAsync);
|
||||
|
||||
// Exception filters only run when there's an exception - unsetting it will short-circuit
|
||||
// other exception filters.
|
||||
await current.FilterAsync.OnExceptionAsync(_exceptionContext);
|
||||
|
||||
_diagnosticSource.AfterOnExceptionAsync(_exceptionContext, current.FilterAsync);
|
||||
|
||||
if (_exceptionContext.Exception == null)
|
||||
{
|
||||
Logger.ExceptionFilterShortCircuited(current.FilterAsync);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (current.Filter != null)
|
||||
{
|
||||
// Exception filters run "on the way out" - so the filter is run after the rest of the
|
||||
// pipeline.
|
||||
await InvokeExceptionFilterAsync();
|
||||
|
||||
Debug.Assert(_exceptionContext != null);
|
||||
if (_exceptionContext.Exception != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnException(_exceptionContext, current.Filter);
|
||||
|
||||
// Exception filters only run when there's an exception - unsetting it will short-circuit
|
||||
// other exception filters.
|
||||
current.Filter.OnException(_exceptionContext);
|
||||
|
||||
_diagnosticSource.AfterOnException(_exceptionContext, current.Filter);
|
||||
|
||||
if (_exceptionContext.Exception == null)
|
||||
{
|
||||
Logger.ExceptionFilterShortCircuited(current.Filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've reached the 'end' of the exception filter pipeline - this means that one stack frame has
|
||||
// been built for each exception. When we return from here, these frames will either:
|
||||
//
|
||||
// 1) Call the filter (if we have an exception)
|
||||
// 2) No-op (if we don't have an exception)
|
||||
Debug.Assert(_exceptionContext == null);
|
||||
_exceptionContext = new ExceptionContext(Context, _filters);
|
||||
|
||||
try
|
||||
{
|
||||
await InvokeAllActionFiltersAsync();
|
||||
|
||||
// Action filters might 'return' an unhandled exception instead of throwing
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
if (_actionExecutedContext.Exception != null && !_actionExecutedContext.ExceptionHandled)
|
||||
{
|
||||
_exceptionContext.Exception = _actionExecutedContext.Exception;
|
||||
if (_actionExecutedContext.ExceptionDispatchInfo != null)
|
||||
{
|
||||
_exceptionContext.ExceptionDispatchInfo = _actionExecutedContext.ExceptionDispatchInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_exceptionContext.ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InvokeAllActionFiltersAsync()
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
Instance = CreateInstance();
|
||||
|
||||
var arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
await BindActionArgumentsAsync(arguments);
|
||||
_actionExecutingContext = new ActionExecutingContext(Context, _filters, arguments, Instance);
|
||||
|
||||
await InvokeActionFilterAsync();
|
||||
}
|
||||
|
||||
private async Task<ActionExecutedContext> InvokeActionFilterAwaitedAsync()
|
||||
{
|
||||
Debug.Assert(_actionExecutingContext != null);
|
||||
if (_actionExecutingContext.Result != null)
|
||||
{
|
||||
// If we get here, it means that an async filter set a result AND called next(). This is forbidden.
|
||||
var message = Resources.FormatAsyncActionFilter_InvalidShortCircuit(
|
||||
typeof(IAsyncActionFilter).Name,
|
||||
nameof(ActionExecutingContext.Result),
|
||||
typeof(ActionExecutingContext).Name,
|
||||
typeof(ActionExecutionDelegate).Name);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
await InvokeActionFilterAsync();
|
||||
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
return _actionExecutedContext;
|
||||
}
|
||||
|
||||
private async Task InvokeActionFilterAsync()
|
||||
{
|
||||
Debug.Assert(_actionExecutingContext != null);
|
||||
|
||||
var item = _cursor.GetNextFilter<IActionFilter, IAsyncActionFilter>();
|
||||
try
|
||||
{
|
||||
if (item.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnActionExecution(_actionExecutingContext, item.FilterAsync);
|
||||
|
||||
await item.FilterAsync.OnActionExecutionAsync(_actionExecutingContext, InvokeActionFilterAwaitedAsync);
|
||||
|
||||
if (_actionExecutedContext == null)
|
||||
{
|
||||
// If we get here then the filter didn't call 'next' indicating a short circuit
|
||||
Logger.ActionFilterShortCircuited(item.FilterAsync);
|
||||
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
Instance)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _actionExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnActionExecution(_actionExecutedContext, item.FilterAsync);
|
||||
}
|
||||
else if (item.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnActionExecuting(_actionExecutingContext, item.Filter);
|
||||
|
||||
item.Filter.OnActionExecuting(_actionExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnActionExecuting(_actionExecutingContext, item.Filter);
|
||||
|
||||
if (_actionExecutingContext.Result != null)
|
||||
{
|
||||
// Short-circuited by setting a result.
|
||||
Logger.ActionFilterShortCircuited(item.Filter);
|
||||
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
Instance)
|
||||
{
|
||||
Canceled = true,
|
||||
Result = _actionExecutingContext.Result,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeActionFilterAsync();
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
|
||||
_diagnosticSource.BeforeOnActionExecuted(_actionExecutedContext, item.Filter);
|
||||
|
||||
item.Filter.OnActionExecuted(_actionExecutedContext);
|
||||
|
||||
_diagnosticSource.BeforeOnActionExecuted(_actionExecutedContext, item.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All action filters have run, execute the action method.
|
||||
IActionResult result = null;
|
||||
|
||||
try
|
||||
{
|
||||
_diagnosticSource.BeforeActionMethod(
|
||||
Context,
|
||||
_actionExecutingContext.ActionArguments,
|
||||
_actionExecutingContext.Controller);
|
||||
|
||||
result = await InvokeActionAsync(_actionExecutingContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_diagnosticSource.AfterActionMethod(
|
||||
Context,
|
||||
_actionExecutingContext.ActionArguments,
|
||||
_actionExecutingContext.Controller,
|
||||
result);
|
||||
}
|
||||
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
Instance)
|
||||
{
|
||||
Result = result
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// Exceptions thrown by the action method OR filters bubble back up through ActionExcecutedContext.
|
||||
_actionExecutedContext = new ActionExecutedContext(
|
||||
_actionExecutingContext,
|
||||
_filters,
|
||||
Instance)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_actionExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task InvokeAllResultFiltersAsync(IActionResult result)
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
_resultExecutingContext = new ResultExecutingContext(Context, _filters, result, Instance);
|
||||
await InvokeResultFilterAsync();
|
||||
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
if (_resultExecutedContext.Exception != null && !_resultExecutedContext.ExceptionHandled)
|
||||
{
|
||||
// There's an unhandled exception in filters
|
||||
if (_resultExecutedContext.ExceptionDispatchInfo != null)
|
||||
{
|
||||
_resultExecutedContext.ExceptionDispatchInfo.Throw();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw _resultExecutedContext.Exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ResultExecutedContext> InvokeResultFilterAwaitedAsync()
|
||||
{
|
||||
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 InvokeResultFilterAsync();
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
return _resultExecutedContext;
|
||||
}
|
||||
|
||||
private async Task InvokeResultFilterAsync()
|
||||
{
|
||||
Debug.Assert(_resultExecutingContext != null);
|
||||
|
||||
try
|
||||
{
|
||||
var item = _cursor.GetNextFilter<IResultFilter, IAsyncResultFilter>();
|
||||
if (item.FilterAsync != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResultExecution(_resultExecutingContext, item.FilterAsync);
|
||||
|
||||
await item.FilterAsync.OnResultExecutionAsync(_resultExecutingContext, InvokeResultFilterAwaitedAsync);
|
||||
|
||||
if (_resultExecutedContext == null || _resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// Short-circuited by not calling next || Short-circuited by setting Cancel == true
|
||||
Logger.ResourceFilterShortCircuited(item.FilterAsync);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
Instance)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
}
|
||||
|
||||
_diagnosticSource.AfterOnResultExecution(_resultExecutedContext, item.FilterAsync);
|
||||
}
|
||||
else if (item.Filter != null)
|
||||
{
|
||||
_diagnosticSource.BeforeOnResultExecuting(_resultExecutingContext, item.Filter);
|
||||
|
||||
item.Filter.OnResultExecuting(_resultExecutingContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuting(_resultExecutingContext, item.Filter);
|
||||
|
||||
if (_resultExecutingContext.Cancel == true)
|
||||
{
|
||||
// Short-circuited by setting Cancel == true
|
||||
Logger.ResourceFilterShortCircuited(item.Filter);
|
||||
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
Instance)
|
||||
{
|
||||
Canceled = true,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
await InvokeResultFilterAsync();
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
|
||||
_diagnosticSource.BeforeOnResultExecuted(_resultExecutedContext, item.Filter);
|
||||
|
||||
item.Filter.OnResultExecuted(_resultExecutedContext);
|
||||
|
||||
_diagnosticSource.AfterOnResultExecuted(_resultExecutedContext, item.Filter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_cursor.Reset();
|
||||
|
||||
// The empty result is always flowed back as the 'executed' result
|
||||
if (_resultExecutingContext.Result == null)
|
||||
{
|
||||
_resultExecutingContext.Result = new EmptyResult();
|
||||
}
|
||||
|
||||
await InvokeResultAsync(_resultExecutingContext.Result);
|
||||
|
||||
Debug.Assert(_resultExecutedContext == null);
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
Instance);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_resultExecutedContext = new ResultExecutedContext(
|
||||
_resultExecutingContext,
|
||||
_filters,
|
||||
_resultExecutingContext.Result,
|
||||
Instance)
|
||||
{
|
||||
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
|
||||
};
|
||||
}
|
||||
|
||||
Debug.Assert(_resultExecutedContext != null);
|
||||
}
|
||||
|
||||
private async Task InvokeResultAsync(IActionResult result)
|
||||
{
|
||||
_diagnosticSource.BeforeActionResult(Context, result);
|
||||
|
||||
try
|
||||
{
|
||||
await result.ExecuteResultAsync(Context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_diagnosticSource.AfterActionResult(Context, result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A one-way cursor for filters.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will iterate the filter collection once per-stage, and skip any filters that don't have
|
||||
/// the one of interfaces that applies to the current stage.
|
||||
///
|
||||
/// Filters are always executed in the following order, but short circuiting plays a role.
|
||||
///
|
||||
/// Indentation reflects nesting.
|
||||
///
|
||||
/// 1. Exception Filters
|
||||
/// 2. Authorization Filters
|
||||
/// 3. Action Filters
|
||||
/// Action
|
||||
///
|
||||
/// 4. Result Filters
|
||||
/// Result
|
||||
///
|
||||
/// </remarks>
|
||||
private struct FilterCursor
|
||||
{
|
||||
private int _index;
|
||||
private readonly IFilterMetadata[] _filters;
|
||||
|
||||
public FilterCursor(int index, IFilterMetadata[] filters)
|
||||
{
|
||||
_index = index;
|
||||
_filters = filters;
|
||||
}
|
||||
|
||||
public FilterCursor(IFilterMetadata[] filters)
|
||||
{
|
||||
_index = 0;
|
||||
_filters = filters;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
public FilterCursorItem<TFilter, TFilterAsync> GetNextFilter<TFilter, TFilterAsync>()
|
||||
where TFilter : class
|
||||
where TFilterAsync : class
|
||||
{
|
||||
while (_index < _filters.Length)
|
||||
{
|
||||
var filter = _filters[_index] as TFilter;
|
||||
var filterAsync = _filters[_index] as TFilterAsync;
|
||||
|
||||
_index += 1;
|
||||
|
||||
if (filter != null || filterAsync != null)
|
||||
{
|
||||
return new FilterCursorItem<TFilter, TFilterAsync>(_index, filter, filterAsync);
|
||||
}
|
||||
}
|
||||
|
||||
return default(FilterCursorItem<TFilter, TFilterAsync>);
|
||||
}
|
||||
}
|
||||
|
||||
private struct FilterCursorItem<TFilter, TFilterAsync>
|
||||
{
|
||||
public readonly int Index;
|
||||
public readonly TFilter Filter;
|
||||
public readonly TFilterAsync FilterAsync;
|
||||
|
||||
public FilterCursorItem(int index, TFilter filter, TFilterAsync filterAsync)
|
||||
{
|
||||
Index = index;
|
||||
Filter = filter;
|
||||
FilterAsync = filterAsync;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2050,14 +2050,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
.Returns(-1000);
|
||||
|
||||
var invoker = new TestControllerActionInvoker(
|
||||
actionContext,
|
||||
new[] { filterProvider.Object },
|
||||
new MockControllerFactory(this),
|
||||
actionDescriptor,
|
||||
argumentBinder.Object,
|
||||
new IValueProviderFactory[0],
|
||||
new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
|
||||
new DiagnosticListener("Microsoft.AspNetCore"),
|
||||
actionContext,
|
||||
new IValueProviderFactory[0],
|
||||
maxAllowedErrorsInModelState);
|
||||
return invoker;
|
||||
}
|
||||
|
|
@ -2100,18 +2099,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var metadataProvider = new EmptyModelMetadataProvider();
|
||||
|
||||
var invoker = new ControllerActionInvoker(
|
||||
actionContext,
|
||||
CreateFilterCache(),
|
||||
controllerFactory.Object,
|
||||
actionDescriptor,
|
||||
new ControllerArgumentBinder(
|
||||
var argumentBinder = new ControllerArgumentBinder(
|
||||
metadataProvider,
|
||||
TestModelBinderFactory.CreateDefault(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new IModelValidatorProvider[0])),
|
||||
new IValueProviderFactory[0],
|
||||
new DefaultObjectValidator(metadataProvider, new IModelValidatorProvider[0]));
|
||||
|
||||
var invoker = new ControllerActionInvoker(
|
||||
CreateFilterCache(),
|
||||
controllerFactory.Object,
|
||||
argumentBinder,
|
||||
new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
|
||||
new DiagnosticListener("Microsoft.AspNetCore"),
|
||||
actionContext,
|
||||
new IValueProviderFactory[0],
|
||||
200);
|
||||
|
||||
// Act
|
||||
|
|
@ -2226,24 +2226,22 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
private class TestControllerActionInvoker : ControllerActionInvoker
|
||||
{
|
||||
public TestControllerActionInvoker(
|
||||
ActionContext actionContext,
|
||||
IFilterProvider[] filterProviders,
|
||||
MockControllerFactory controllerFactory,
|
||||
ControllerActionDescriptor descriptor,
|
||||
IControllerArgumentBinder argumentBinder,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
ActionContext actionContext,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
int maxAllowedErrorsInModelState)
|
||||
: base(
|
||||
actionContext,
|
||||
CreateFilterCache(filterProviders),
|
||||
controllerFactory,
|
||||
descriptor,
|
||||
argumentBinder,
|
||||
valueProviderFactories,
|
||||
logger,
|
||||
diagnosticSource,
|
||||
actionContext,
|
||||
valueProviderFactories,
|
||||
maxAllowedErrorsInModelState)
|
||||
{
|
||||
ControllerFactory = controllerFactory;
|
||||
|
|
|
|||
Loading…
Reference in New Issue