Add mapping service for action results
This allows the use of custom 'envelope' types like ActionResult<> with a corresponding API Explorer implementation. Basically this PR services to decouple a bunch of infrastructure from ActionResult<>.
This commit is contained in:
parent
927af3125e
commit
c93c168df3
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
|
@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
public class DefaultApiDescriptionProvider : IApiDescriptionProvider
|
||||
{
|
||||
private readonly MvcOptions _mvcOptions;
|
||||
private readonly IActionResultTypeMapper _mapper;
|
||||
private readonly IInlineConstraintResolver _constraintResolver;
|
||||
private readonly IModelMetadataProvider _modelMetadataProvider;
|
||||
|
||||
|
|
@ -35,6 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> used for resolving inline
|
||||
/// constraints.</param>
|
||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future release.")]
|
||||
public DefaultApiDescriptionProvider(
|
||||
IOptions<MvcOptions> optionsAccessor,
|
||||
IInlineConstraintResolver constraintResolver,
|
||||
|
|
@ -45,6 +48,26 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
_modelMetadataProvider = modelMetadataProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DefaultApiDescriptionProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="optionsAccessor">The accessor for <see cref="MvcOptions"/>.</param>
|
||||
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/> used for resolving inline
|
||||
/// constraints.</param>
|
||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||
/// <param name="mapper"> The <see cref="IActionResultTypeMapper"/>.</param>
|
||||
public DefaultApiDescriptionProvider(
|
||||
IOptions<MvcOptions> optionsAccessor,
|
||||
IInlineConstraintResolver constraintResolver,
|
||||
IModelMetadataProvider modelMetadataProvider,
|
||||
IActionResultTypeMapper mapper)
|
||||
{
|
||||
_mvcOptions = optionsAccessor.Value;
|
||||
_constraintResolver = constraintResolver;
|
||||
_modelMetadataProvider = modelMetadataProvider;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Order => -1000;
|
||||
|
||||
|
|
@ -481,12 +504,14 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
{
|
||||
return typeof(void);
|
||||
}
|
||||
|
||||
|
||||
// Unwrap the type if it's a Task<T>. The Task (non-generic) case was already handled.
|
||||
var unwrappedType = UnwrapGenericType(declaredReturnType, typeof(Task<>));
|
||||
|
||||
// Unwrap the type if it's ActionResult<T> or Task<ActionResult<T>>.
|
||||
unwrappedType = UnwrapGenericType(unwrappedType, typeof(ActionResult<>));
|
||||
Type unwrappedType = declaredReturnType;
|
||||
if (declaredReturnType.IsGenericType &&
|
||||
declaredReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
unwrappedType = declaredReturnType.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
// If the method is declared to return IActionResult or a derived class, that information
|
||||
// isn't valuable to the formatter.
|
||||
|
|
@ -494,16 +519,11 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return unwrappedType;
|
||||
}
|
||||
|
||||
Type UnwrapGenericType(Type type, Type queryType)
|
||||
{
|
||||
var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, queryType);
|
||||
return genericType?.GenericTypeArguments[0] ?? type;
|
||||
}
|
||||
// If we get here, the type should be a user-defined data type or an envelope type
|
||||
// like ActionResult<T>. The mapper service will unwrap envelopes.
|
||||
unwrappedType = _mapper.GetResultDataType(unwrappedType);
|
||||
return unwrappedType;
|
||||
}
|
||||
|
||||
private Type GetRuntimeReturnType(Type declaredReturnType)
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.TryAddSingleton<ControllerActionInvokerCache>();
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
|
||||
services.TryAddSingleton<IActionResultTypeMapper, ActionResultTypeMapper>();
|
||||
|
||||
//
|
||||
// Request body limit filters
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a mapping from the return value of an action to an <see cref="IActionResult"/>
|
||||
/// for request processing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default implementation of this service handles the conversion of
|
||||
/// <see cref="ActionResult{TValue}"/> to an <see cref="IActionResult"/> during request
|
||||
/// processing as well as the mapping of <see cref="ActionResult{TValue}"/> to <c>TValue</c>
|
||||
/// during API Explorer processing.
|
||||
/// </remarks>
|
||||
public interface IActionResultTypeMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the result data type that corresponds to <paramref name="returnType"/>. This
|
||||
/// method will not be called for actions that return <c>void</c> or an <see cref="IActionResult"/>
|
||||
/// type.
|
||||
/// </summary>
|
||||
/// <param name="returnType">The declared return type of an action.</param>
|
||||
/// <returns>A <see cref="Type"/> that represents the response data.</returns>
|
||||
/// <remarks>
|
||||
/// Prior to calling this method, the infrastructure will unwrap <see cref="Task{TResult}"/> or
|
||||
/// other task-like types.
|
||||
/// </remarks>
|
||||
Type GetResultDataType(Type returnType);
|
||||
|
||||
/// <summary>
|
||||
/// Converts the result of an action to an <see cref="IActionResult"/> for response processing.
|
||||
/// This method will be not be called when a method returns <c>void</c> or an
|
||||
/// <see cref="IActionResult"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The action return value. May be <c>null</c>.</param>
|
||||
/// <param name="returnType">The declared return type.</param>
|
||||
/// <returns>An <see cref="IActionResult"/> for response processing.</returns>
|
||||
/// <remarks>
|
||||
/// Prior to calling this method, the infrastructure will unwrap <see cref="Task{TResult}"/> or
|
||||
/// other task-like types.
|
||||
/// </remarks>
|
||||
IActionResult Convert(object value, Type returnType);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
new AwaitableObjectResultExecutor(),
|
||||
};
|
||||
|
||||
public abstract ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments);
|
||||
public abstract ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments);
|
||||
|
||||
protected abstract bool CanExecute(ObjectMethodExecutor executor);
|
||||
|
||||
|
|
@ -48,7 +52,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// void LogMessage(..)
|
||||
private class VoidResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
executor.Execute(controller, arguments);
|
||||
return new ValueTask<IActionResult>(new EmptyResult());
|
||||
|
|
@ -62,7 +70,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// CreatedAtResult Put(..)
|
||||
private class SyncActionResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
var actionResult = (IActionResult)executor.Execute(controller, arguments);
|
||||
EnsureActionResultNotNull(executor, actionResult);
|
||||
|
|
@ -78,11 +90,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// object Index(..)
|
||||
private class SyncObjectResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
// Sync method returning arbitrary object
|
||||
var returnValue = executor.Execute(controller, arguments);
|
||||
var actionResult = ConvertToActionResult(returnValue, executor.MethodReturnType);
|
||||
var actionResult = ConvertToActionResult(mapper, returnValue, executor.MethodReturnType);
|
||||
return new ValueTask<IActionResult>(actionResult);
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +109,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Task SaveState(..)
|
||||
private class TaskResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override async ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override async ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
await (Task)executor.Execute(controller, arguments);
|
||||
return new EmptyResult();
|
||||
|
|
@ -106,7 +126,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Custom task-like type with no return value.
|
||||
private class AwaitableResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override async ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override async ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
await executor.ExecuteAsync(controller, arguments);
|
||||
return new EmptyResult();
|
||||
|
|
@ -122,7 +146,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Task<IActionResult> Post(..)
|
||||
private class TaskOfIActionResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override async ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override async ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
// Async method returning Task<IActionResult>
|
||||
// Avoid extra allocations by calling Execute rather than ExecuteAsync and casting to Task<IActionResult>.
|
||||
|
|
@ -141,7 +169,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// ValueTask<ViewResult> GetViewsAsync(..)
|
||||
private class TaskOfActionResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override async ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override async ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
// Async method returning awaitable-of-IActionResult (e.g., Task<ViewResult>)
|
||||
// We have to use ExecuteAsync because we don't know the awaitable's type at compile time.
|
||||
|
|
@ -161,11 +193,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Task<Customer> GetCustomerAsync(..)
|
||||
private class AwaitableObjectResultExecutor : ActionMethodExecutor
|
||||
{
|
||||
public override async ValueTask<IActionResult> Execute(ObjectMethodExecutor executor, object controller, object[] arguments)
|
||||
public override async ValueTask<IActionResult> Execute(
|
||||
IActionResultTypeMapper mapper,
|
||||
ObjectMethodExecutor executor,
|
||||
object controller,
|
||||
object[] arguments)
|
||||
{
|
||||
// Async method returning awaitable-of-nonvoid
|
||||
var returnValue = await executor.ExecuteAsync(controller, arguments);
|
||||
var actionResult = ConvertToActionResult(returnValue, executor.MethodReturnType);
|
||||
var actionResult = ConvertToActionResult(mapper, returnValue, executor.MethodReturnType);
|
||||
return actionResult;
|
||||
}
|
||||
|
||||
|
|
@ -176,30 +212,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
if (actionResult == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatActionResult_ActionReturnValueCannotBeNull(executor.AsyncResultType ?? executor.MethodReturnType));
|
||||
var type = executor.AsyncResultType ?? executor.MethodReturnType;
|
||||
throw new InvalidOperationException(Resources.FormatActionResult_ActionReturnValueCannotBeNull(type));
|
||||
}
|
||||
}
|
||||
|
||||
private static IActionResult ConvertToActionResult(object returnValue, Type declaredType)
|
||||
private IActionResult ConvertToActionResult(IActionResultTypeMapper mapper, object returnValue, Type declaredType)
|
||||
{
|
||||
IActionResult result;
|
||||
switch (returnValue)
|
||||
{
|
||||
case IActionResult actionResult:
|
||||
result = actionResult;
|
||||
break;
|
||||
case IConvertToActionResult convertToActionResult:
|
||||
result = convertToActionResult.Convert();
|
||||
break;
|
||||
default:
|
||||
result = new ObjectResult(returnValue)
|
||||
{
|
||||
DeclaredType = declaredType,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
var result = (returnValue as IActionResult) ?? mapper.Convert(returnValue, declaredType);
|
||||
if (result == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatActionResult_ActionReturnValueCannotBeNull(declaredType));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// 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 Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class ActionResultTypeMapper : IActionResultTypeMapper
|
||||
{
|
||||
public Type GetResultDataType(Type returnType)
|
||||
{
|
||||
if (returnType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(returnType));
|
||||
}
|
||||
|
||||
if (returnType.IsGenericType &&
|
||||
returnType.GetGenericTypeDefinition() == typeof(ActionResult<>))
|
||||
{
|
||||
return returnType.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public IActionResult Convert(object value, Type returnType)
|
||||
{
|
||||
if (returnType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(returnType));
|
||||
}
|
||||
|
||||
if (value is IConvertToActionResult converter)
|
||||
{
|
||||
return converter.Convert();
|
||||
}
|
||||
|
||||
return new ObjectResult(value)
|
||||
{
|
||||
DeclaredType = returnType,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,10 +28,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
internal ControllerActionInvoker(
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
IActionResultTypeMapper mapper,
|
||||
ControllerContext controllerContext,
|
||||
ControllerActionInvokerCacheEntry cacheEntry,
|
||||
IFilterMetadata[] filters)
|
||||
: base(diagnosticSource, logger, controllerContext, filters, controllerContext.ValueProviderFactories)
|
||||
: base(diagnosticSource, logger, mapper, controllerContext, filters, controllerContext.ValueProviderFactories)
|
||||
{
|
||||
if (cacheEntry == null)
|
||||
{
|
||||
|
|
@ -347,7 +348,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controller);
|
||||
logger.ActionMethodExecuting(controllerContext, orderedArguments);
|
||||
var stopwatch = ValueStopwatch.StartNew();
|
||||
var actionResultValueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, orderedArguments);
|
||||
var actionResultValueTask = actionMethodExecutor.Execute(_mapper, objectMethodExecutor, controller, orderedArguments);
|
||||
if (actionResultValueTask.IsCompletedSuccessfully)
|
||||
{
|
||||
result = actionResultValueTask.Result;
|
||||
|
|
@ -370,18 +371,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsResultIActionResult(ObjectMethodExecutor executor)
|
||||
{
|
||||
var resultType = executor.AsyncResultType ?? executor.MethodReturnType;
|
||||
return typeof(IActionResult).IsAssignableFrom(resultType);
|
||||
}
|
||||
|
||||
private bool IsConvertibleToActionResult(ObjectMethodExecutor executor)
|
||||
{
|
||||
var resultType = executor.AsyncResultType ?? executor.MethodReturnType;
|
||||
return typeof(IConvertToActionResult).IsAssignableFrom(resultType);
|
||||
}
|
||||
|
||||
/// <remarks><see cref="ResourceInvoker.InvokeFilterPipelineAsync"/> for details on what the
|
||||
/// variables in this method represent.</remarks>
|
||||
protected override async Task InvokeInnerFilterAsync()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -20,18 +21,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
private readonly int _maxModelValidationErrors;
|
||||
private readonly ILogger _logger;
|
||||
private readonly DiagnosticSource _diagnosticSource;
|
||||
private readonly IActionResultTypeMapper _mapper;
|
||||
|
||||
public ControllerActionInvokerProvider(
|
||||
ControllerActionInvokerCache controllerActionInvokerCache,
|
||||
IOptions<MvcOptions> optionsAccessor,
|
||||
ILoggerFactory loggerFactory,
|
||||
DiagnosticSource diagnosticSource)
|
||||
DiagnosticSource diagnosticSource,
|
||||
IActionResultTypeMapper mapper)
|
||||
{
|
||||
_controllerActionInvokerCache = controllerActionInvokerCache;
|
||||
_valueProviderFactories = optionsAccessor.Value.ValueProviderFactories.ToArray();
|
||||
_maxModelValidationErrors = optionsAccessor.Value.MaxModelValidationErrors;
|
||||
_logger = loggerFactory.CreateLogger<ControllerActionInvoker>();
|
||||
_diagnosticSource = diagnosticSource;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public int Order => -1000;
|
||||
|
|
@ -56,6 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var invoker = new ControllerActionInvoker(
|
||||
_logger,
|
||||
_diagnosticSource,
|
||||
_mapper,
|
||||
controllerContext,
|
||||
cacheResult.cacheEntry,
|
||||
cacheResult.filters);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Runtime.ExceptionServices;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -18,6 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
protected readonly DiagnosticSource _diagnosticSource;
|
||||
protected readonly ILogger _logger;
|
||||
protected readonly IActionResultTypeMapper _mapper;
|
||||
protected readonly ActionContext _actionContext;
|
||||
protected readonly IFilterMetadata[] _filters;
|
||||
protected readonly IList<IValueProviderFactory> _valueProviderFactories;
|
||||
|
|
@ -38,12 +40,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
public ResourceInvoker(
|
||||
DiagnosticSource diagnosticSource,
|
||||
ILogger logger,
|
||||
IActionResultTypeMapper mapper,
|
||||
ActionContext actionContext,
|
||||
IFilterMetadata[] filters,
|
||||
IList<IValueProviderFactory> valueProviderFactories)
|
||||
{
|
||||
_diagnosticSource = diagnosticSource ?? throw new ArgumentNullException(nameof(diagnosticSource));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
_actionContext = actionContext ?? throw new ArgumentNullException(nameof(actionContext));
|
||||
|
||||
_filters = filters ?? throw new ArgumentNullException(nameof(filters));
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Runtime.ExceptionServices;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
|
|
@ -43,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
IPageHandlerMethodSelector handlerMethodSelector,
|
||||
DiagnosticSource diagnosticSource,
|
||||
ILogger logger,
|
||||
IActionResultTypeMapper mapper,
|
||||
PageContext pageContext,
|
||||
IFilterMetadata[] filterMetadata,
|
||||
PageActionInvokerCacheEntry cacheEntry,
|
||||
|
|
@ -52,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
: base(
|
||||
diagnosticSource,
|
||||
logger,
|
||||
mapper,
|
||||
pageContext,
|
||||
filterMetadata,
|
||||
pageContext.ValueProviderFactories)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
private readonly RazorProjectFileSystem _razorFileSystem;
|
||||
private readonly DiagnosticSource _diagnosticSource;
|
||||
private readonly ILogger<PageActionInvoker> _logger;
|
||||
private readonly IActionResultTypeMapper _mapper;
|
||||
private volatile InnerCache _currentCache;
|
||||
|
||||
public PageActionInvokerProvider(
|
||||
|
|
@ -60,7 +61,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
IPageHandlerMethodSelector selector,
|
||||
RazorProjectFileSystem razorFileSystem,
|
||||
DiagnosticSource diagnosticSource,
|
||||
ILoggerFactory loggerFactory)
|
||||
ILoggerFactory loggerFactory,
|
||||
IActionResultTypeMapper mapper)
|
||||
{
|
||||
_loader = loader;
|
||||
_pageFactoryProvider = pageFactoryProvider;
|
||||
|
|
@ -79,6 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_razorFileSystem = razorFileSystem;
|
||||
_diagnosticSource = diagnosticSource;
|
||||
_logger = loggerFactory.CreateLogger<PageActionInvoker>();
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public int Order { get; } = -1000;
|
||||
|
|
@ -158,6 +161,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_selector,
|
||||
_diagnosticSource,
|
||||
_logger,
|
||||
_mapper,
|
||||
pageContext,
|
||||
filters,
|
||||
cacheEntry,
|
||||
|
|
|
|||
|
|
@ -1449,7 +1449,8 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
|||
var provider = new DefaultApiDescriptionProvider(
|
||||
optionsAccessor,
|
||||
constraintResolver.Object,
|
||||
modelMetadataProvider);
|
||||
modelMetadataProvider,
|
||||
new ActionResultTypeMapper());
|
||||
|
||||
provider.OnProvidersExecuting(context);
|
||||
provider.OnProvidersExecuted(context);
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesVoidActions()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.VoidAction));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.True(controller.Executed);
|
||||
|
|
@ -33,12 +34,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturningIActionResult()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnIActionResult));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.True(valueTask.IsCompleted);
|
||||
|
|
@ -49,12 +51,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturningSubTypeOfActionResult()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsIActionResultSubType));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.IsType<PartialViewResult>(valueTask.Result);
|
||||
|
|
@ -64,12 +67,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturningActionResultOfT()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsActionResultOfT));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
var result = Assert.IsType<ObjectResult>(valueTask.Result);
|
||||
|
|
@ -81,12 +85,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturningModelAsModel()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsModelAsModel));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
var result = Assert.IsType<ObjectResult>(valueTask.Result);
|
||||
|
|
@ -98,12 +103,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturningModelAsObject()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnModelAsObject));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
var result = Assert.IsType<ObjectResult>(valueTask.Result);
|
||||
|
|
@ -115,12 +121,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturningActionResultAsObject()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsIActionResultSubType));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.IsType<PartialViewResult>(valueTask.Result);
|
||||
|
|
@ -130,12 +137,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsReturnTask()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsTask));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.True(controller.Executed);
|
||||
|
|
@ -146,12 +154,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutorExecutesActionsAsynchronouslyReturningIActionResult()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnIActionResultAsync));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.IsType<ViewResult>(valueTask.Result);
|
||||
|
|
@ -161,12 +170,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public async Task ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningActionResultSubType()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnIActionResultAsync));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
await valueTask;
|
||||
|
|
@ -177,12 +187,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningModel()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsModelAsModelAsync));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
var result = Assert.IsType<ObjectResult>(valueTask.Result);
|
||||
|
|
@ -194,12 +205,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningModelAsObject()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsModelAsObjectAsync));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
var result = Assert.IsType<ObjectResult>(valueTask.Result);
|
||||
|
|
@ -211,12 +223,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningIActionResultAsObject()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnIActionResultAsObjectAsync));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
Assert.IsType<OkResult>(valueTask.Result);
|
||||
|
|
@ -226,12 +239,13 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningActionResultOfT()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnActionResultOFTAsync));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act
|
||||
var valueTask = actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>());
|
||||
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
|
||||
|
||||
// Assert
|
||||
var result = Assert.IsType<ObjectResult>(valueTask.Result);
|
||||
|
|
@ -243,13 +257,14 @@ namespace Microsoft.AspNetCore.Mvc.Core.Internal
|
|||
public void ActionMethodExecutor_ThrowsIfIConvertFromIActionResult_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
var controller = new TestController();
|
||||
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsCustomConvertibleFromIActionResult));
|
||||
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
() => actionMethodExecutor.Execute(objectMethodExecutor, controller, Array.Empty<object>()));
|
||||
() => actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>()));
|
||||
|
||||
Assert.Equal($"Cannot return null from an action method with a return type of '{typeof(CustomConvertibleFromAction)}'.", ex.Message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// 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 Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class ActionResultTypeMapperTest
|
||||
{
|
||||
[Fact]
|
||||
public void Convert_WithIConvertToActionResult_DelegatesToInterface()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
|
||||
var expected = new EmptyResult();
|
||||
var returnValue = Mock.Of<IConvertToActionResult>(r => r.Convert() == expected);
|
||||
|
||||
// Act
|
||||
var result = mapper.Convert(returnValue, typeof(string));
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Convert_WithRegularType_CreatesObjectResult()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
|
||||
var returnValue = "hello";
|
||||
|
||||
// Act
|
||||
var result = mapper.Convert(returnValue, typeof(string));
|
||||
|
||||
// Assert
|
||||
var objectResult = Assert.IsType<ObjectResult>(result);
|
||||
Assert.Same(returnValue, objectResult.Value);
|
||||
Assert.Equal(typeof(string), objectResult.DeclaredType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetResultDataType_WithActionResultOfT_UnwrapsType()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
|
||||
var returnType = typeof(ActionResult<string>);
|
||||
|
||||
// Act
|
||||
var result = mapper.GetResultDataType(returnType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(typeof(string), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetResultDataType_WithRegularType_ReturnsType()
|
||||
{
|
||||
// Arrange
|
||||
var mapper = new ActionResultTypeMapper();
|
||||
|
||||
var returnType = typeof(string);
|
||||
|
||||
// Act
|
||||
var result = mapper.GetResultDataType(returnType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(typeof(string), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1345,6 +1345,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var invoker = new ControllerActionInvoker(
|
||||
new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
|
||||
new DiagnosticListener("Microsoft.AspNetCore"),
|
||||
new ActionResultTypeMapper(),
|
||||
controllerContext,
|
||||
cacheEntry,
|
||||
new IFilterMetadata[0]);
|
||||
|
|
@ -1624,6 +1625,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var invoker = new ControllerActionInvoker(
|
||||
logger,
|
||||
diagnosticSource,
|
||||
new ActionResultTypeMapper(),
|
||||
controllerContext,
|
||||
cacheEntry,
|
||||
filters);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
|
@ -285,6 +286,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
new MockControllerFactory(controller ?? this),
|
||||
new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
|
||||
diagnosticSource,
|
||||
new ActionResultTypeMapper(),
|
||||
actionContext,
|
||||
new List<IValueProviderFactory>(),
|
||||
maxAllowedErrorsInModelState: 200);
|
||||
|
|
@ -389,12 +391,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
MockControllerFactory controllerFactory,
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
IActionResultTypeMapper mapper,
|
||||
ActionContext actionContext,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
int maxAllowedErrorsInModelState)
|
||||
: base(
|
||||
logger,
|
||||
diagnosticSource,
|
||||
mapper,
|
||||
CreatControllerContext(actionContext, valueProviderFactories, maxAllowedErrorsInModelState),
|
||||
CreateCacheEntry((ControllerActionDescriptor)actionContext.ActionDescriptor, controllerFactory),
|
||||
filters)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
|
@ -515,7 +516,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Mock.Of<IPageHandlerMethodSelector>(),
|
||||
fileSystem,
|
||||
new DiagnosticListener("Microsoft.AspNetCore"),
|
||||
NullLoggerFactory.Instance);
|
||||
NullLoggerFactory.Instance,
|
||||
new ActionResultTypeMapper());
|
||||
}
|
||||
|
||||
private IActionDescriptorCollectionProvider CreateActionDescriptorCollection(PageActionDescriptor descriptor)
|
||||
|
|
|
|||
|
|
@ -1217,6 +1217,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
selector.Object,
|
||||
diagnosticListener ?? new DiagnosticListener("Microsoft.AspNetCore"),
|
||||
logger ?? NullLogger.Instance,
|
||||
new ActionResultTypeMapper(),
|
||||
pageContext,
|
||||
filters ?? Array.Empty<IFilterMetadata>(),
|
||||
cacheEntry,
|
||||
|
|
|
|||
Loading…
Reference in New Issue