diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvoker.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvoker.cs index 084ec71ef6..404c9267eb 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvoker.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvoker.cs @@ -641,7 +641,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal else if (!_executor.IsMethodAsync) { var resultAsObject = _executor.Execute(_controller, arguments); - result = new ObjectResult(resultAsObject) + result = resultAsObject as IActionResult ?? new ObjectResult(resultAsObject) { DeclaredType = returnType, }; @@ -649,7 +649,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal else if (_executor.TaskGenericType != null) { var resultAsObject = await _executor.ExecuteAsync(_controller, arguments); - result = new ObjectResult(resultAsObject) + result = resultAsObject as IActionResult ?? new ObjectResult(resultAsObject) { DeclaredType = _executor.TaskGenericType, }; diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs index c695754142..a8eee7f282 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs @@ -2356,6 +2356,58 @@ namespace Microsoft.AspNetCore.Mvc.Internal Assert.IsType(result); } + [Fact] + public async Task InvokeAction_AsyncActionWithTaskOfObjectReturnType_AndReturningTaskOfActionResult() + { + // Arrange + var actionParameters = new Dictionary { ["value"] = 3 }; + IActionResult result = null; + + var filter = new Mock(MockBehavior.Strict); + filter.Setup(f => f.OnActionExecuting(It.IsAny())).Verifiable(); + filter + .Setup(f => f.OnActionExecuted(It.IsAny())) + .Callback(c => result = c.Result); + + var invoker = CreateInvoker( + new[] { filter.Object }, + nameof(TestController.AsyncActionMethodReturningActionResultWithTaskOfObjectAsReturnType), + actionParameters); + + // Act + await invoker.InvokeAsync(); + + // Assert + var testResult = Assert.IsType(result); + Assert.Equal(3, testResult.Value); + } + + [Fact] + public async Task InvokeAction_ActionWithObjectReturnType_AndReturningActionResult() + { + // Arrange + var actionParameters = new Dictionary { ["value"] = 3 }; + IActionResult result = null; + + var filter = new Mock(MockBehavior.Strict); + filter.Setup(f => f.OnActionExecuting(It.IsAny())).Verifiable(); + filter + .Setup(f => f.OnActionExecuted(It.IsAny())) + .Callback(c => result = c.Result); + + var invoker = CreateInvoker( + new[] { filter.Object }, + nameof(TestController.ActionMethodReturningActionResultWithObjectAsReturnType), + actionParameters); + + // Act + await invoker.InvokeAsync(); + + // Assert + var testResult = Assert.IsType(result); + Assert.Equal(3, testResult.Value); + } + [Fact] public async Task InvokeAction_AsyncMethod_ParametersInRandomOrder() { @@ -2908,6 +2960,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal return null; } + public object ActionMethodReturningActionResultWithObjectAsReturnType(int value = 5) + { + return new TestActionResult { Value = value }; + } + + public async Task AsyncActionMethodReturningActionResultWithTaskOfObjectAsReturnType(int value = 5) + { + return await Task.FromResult(new TestActionResult { Value = value }); + } + public TestActionResult TestActionMethodWithNullActionResult() { return null; diff --git a/test/WebSites/BasicWebSite/Controllers/HomeController.cs b/test/WebSites/BasicWebSite/Controllers/HomeController.cs index fa2a854aae..48e1aa850c 100644 --- a/test/WebSites/BasicWebSite/Controllers/HomeController.cs +++ b/test/WebSites/BasicWebSite/Controllers/HomeController.cs @@ -16,7 +16,9 @@ namespace BasicWebSite.Controllers return View(); } - public IActionResult PlainView() + // Keep the return type as object to ensure that we don't + // wrap IActionResult instances into ObjectResults. + public object PlainView() { return View(); }