aspnetcore/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvoker.cs

168 lines
5.8 KiB
C#

// 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;
#if NETSTANDARD1_3
using System.Reflection;
#endif
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.Internal
{
public class ControllerActionInvoker : FilterActionInvoker
{
private readonly ControllerActionDescriptor _descriptor;
private readonly IControllerFactory _controllerFactory;
private readonly IControllerActionArgumentBinder _argumentBinder;
public ControllerActionInvoker(
ActionContext actionContext,
FilterCache filterCache,
IControllerFactory controllerFactory,
ControllerActionDescriptor descriptor,
IReadOnlyList<IInputFormatter> inputFormatters,
IControllerActionArgumentBinder argumentBinder,
IReadOnlyList<IModelBinder> modelBinders,
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
ILogger logger,
DiagnosticSource diagnosticSource,
int maxModelValidationErrors)
: base(
actionContext,
filterCache,
inputFormatters,
modelBinders,
modelValidatorProviders,
valueProviderFactories,
logger,
diagnosticSource,
maxModelValidationErrors)
{
if (controllerFactory == null)
{
throw new ArgumentNullException(nameof(controllerFactory));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
if (argumentBinder == null)
{
throw new ArgumentNullException(nameof(argumentBinder));
}
_controllerFactory = controllerFactory;
_descriptor = descriptor;
_argumentBinder = argumentBinder;
if (descriptor.MethodInfo == null)
{
throw new ArgumentException(
Resources.FormatPropertyOfTypeCannotBeNull(
nameof(descriptor.MethodInfo),
typeof(ControllerActionDescriptor)),
nameof(descriptor));
}
}
protected override object CreateInstance()
{
return _controllerFactory.CreateController(Context);
}
protected override void ReleaseInstance(object instance)
{
_controllerFactory.ReleaseController(Context, instance);
}
protected override async Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext)
{
if (actionExecutingContext == null)
{
throw new ArgumentNullException(nameof(actionExecutingContext));
}
var actionMethodInfo = _descriptor.MethodInfo;
var arguments = ControllerActionExecutor.PrepareArguments(
actionExecutingContext.ActionArguments,
actionMethodInfo.GetParameters());
Logger.ActionMethodExecuting(actionExecutingContext, arguments);
var actionReturnValue = await ControllerActionExecutor.ExecuteAsync(
actionMethodInfo,
actionExecutingContext.Controller,
arguments);
var actionResult = CreateActionResult(
actionMethodInfo.ReturnType,
actionReturnValue);
Logger.ActionMethodExecuted(actionExecutingContext, actionResult);
return actionResult;
}
protected override Task<IDictionary<string, object>> BindActionArgumentsAsync()
{
return _argumentBinder.BindActionArgumentsAsync(Context, Instance);
}
// Marking as internal for Unit Testing purposes.
internal static IActionResult CreateActionResult(Type declaredReturnType, object actionReturnValue)
{
if (declaredReturnType == null)
{
throw new ArgumentNullException(nameof(declaredReturnType));
}
// optimize common path
var actionResult = actionReturnValue as IActionResult;
if (actionResult != null)
{
return actionResult;
}
if (declaredReturnType == typeof(void) ||
declaredReturnType == typeof(Task))
{
return new EmptyResult();
}
// Unwrap potential Task<T> types.
var actualReturnType = GetTaskInnerTypeOrNull(declaredReturnType) ?? declaredReturnType;
if (actionReturnValue == null &&
typeof(IActionResult).IsAssignableFrom(actualReturnType))
{
throw new InvalidOperationException(
Resources.FormatActionResult_ActionReturnValueCannotBeNull(actualReturnType));
}
return new ObjectResult(actionReturnValue)
{
DeclaredType = actualReturnType
};
}
private static Type GetTaskInnerTypeOrNull(Type type)
{
var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(Task<>));
return genericType?.GenericTypeArguments[0];
}
}
}