CreateActionResult should work when action returns Task.

Fixes #647
This commit is contained in:
Pranav K 2014-06-16 07:55:57 -07:00
parent d79943d30f
commit 85bd056780
4 changed files with 77 additions and 54 deletions

View File

@ -101,23 +101,25 @@ namespace Microsoft.AspNet.Mvc
{ {
// optimize common path // optimize common path
var actionResult = actionReturnValue as IActionResult; var actionResult = actionReturnValue as IActionResult;
if (actionResult != null) if (actionResult != null)
{ {
return actionResult; return actionResult;
} }
if (actionReturnValue == null && typeof(IActionResult).IsAssignableFrom(declaredReturnType)) if (declaredReturnType == typeof(void) ||
{ declaredReturnType == typeof(Task))
throw new InvalidOperationException(
Resources.FormatActionResult_ActionReturnValueCannotBeNull(declaredReturnType));
}
if (declaredReturnType == typeof(void))
{ {
return new NoContentResult(); return new NoContentResult();
} }
// Unwrap potential Task<T> types.
var actualReturnType = TypeHelper.GetTaskInnerTypeOrNull(declaredReturnType) ?? declaredReturnType;
if (actionReturnValue == null && typeof(IActionResult).IsAssignableFrom(actualReturnType))
{
throw new InvalidOperationException(
Resources.FormatActionResult_ActionReturnValueCannotBeNull(actualReturnType));
}
return new ObjectContentResult(actionReturnValue); return new ObjectContentResult(actionReturnValue);
} }
@ -400,12 +402,8 @@ namespace Microsoft.AspNet.Mvc
_actionContext.Controller, _actionContext.Controller,
_actionExecutingContext.ActionArguments); _actionExecutingContext.ActionArguments);
var underlyingReturnType =
TypeHelper.GetTaskInnerTypeOrNull(actionMethodInfo.ReturnType) ??
actionMethodInfo.ReturnType;
var actionResult = CreateActionResult( var actionResult = CreateActionResult(
underlyingReturnType, actionMethodInfo.ReturnType,
actionReturnValue); actionReturnValue);
return actionResult; return actionResult;
} }

View File

@ -29,22 +29,6 @@ namespace Microsoft.AspNet.Mvc
public int x; public int x;
} }
public static IEnumerable<object[]> CreateActionResultData
{
get
{
yield return new object[] { new { x1 = 10, y1 = "Hello" } };
yield return new object[] { 5 };
yield return new object[] { "sample input" };
SampleStruct test;
test.x = 10;
yield return new object[] { test };
yield return new object[] { new Task(() => Console.WriteLine("Test task")) };
}
}
[Fact] [Fact]
public async Task InvokeAction_DoesNotInvokeExceptionFilter_WhenActionDoesNotThrow() public async Task InvokeAction_DoesNotInvokeExceptionFilter_WhenActionDoesNotThrow()
{ {
@ -1241,28 +1225,44 @@ namespace Microsoft.AspNet.Mvc
} }
[Theory] [Theory]
[InlineData(typeof(void), typeof(NoContentResult))] [InlineData(typeof(void))]
[InlineData(typeof(string), typeof(ObjectContentResult))] [InlineData(typeof(Task))]
public void CreateActionResult_Types_ReturnsAppropriateResults(Type type, Type returnType) public void CreateActionResult_Types_ReturnsNoContentResultForTaskAndVoidReturnTypes(Type type)
{ {
// Arrange & Act // Arrange & Act
var result = ReflectedActionInvoker.CreateActionResult(type, null).GetType(); var result = ReflectedActionInvoker.CreateActionResult(type, null).GetType();
// Assert // Assert
Assert.Equal(returnType, result); Assert.Equal(typeof(NoContentResult), (result));
}
public static IEnumerable<object[]> CreateActionResult_ReturnsObjectContentResultData
{
get
{
var anonymousObject = new { x1 = 10, y1 = "Hello" };
yield return new object[] { anonymousObject.GetType(), anonymousObject, };
yield return new object[] { typeof(int), 5 };
yield return new object[] { typeof(string), "sample input" };
SampleStruct test;
test.x = 10;
yield return new object[] { test.GetType(), test };
yield return new object[] { typeof(Task<int>), 5 };
yield return new object[] { typeof(Task<string>), "Hello world" };
}
} }
[Theory] [Theory]
[MemberData("CreateActionResultData")] [MemberData("CreateActionResult_ReturnsObjectContentResultData")]
public void CreateActionResult_ReturnsObjectContentResult(object input) public void CreateActionResult_ReturnsObjectContentResult(Type type, object input)
{ {
// Arrange & Act // Arrange & Act
var actualResult = ReflectedActionInvoker.CreateActionResult(input.GetType(), input) var actualResult = ReflectedActionInvoker.CreateActionResult(type, input);
as ObjectContentResult;
// Assert // Assert
Assert.NotNull(actualResult); var contentResult = Assert.IsType<ObjectContentResult>(actualResult);
Assert.Equal(input, actualResult.Value); Assert.Same(input, contentResult.Value);
} }
private ReflectedActionInvoker CreateInvoker(IFilter filter, bool actionThrows = false) private ReflectedActionInvoker CreateInvoker(IFilter filter, bool actionThrows = false)

View File

@ -90,6 +90,22 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(0, result.Body.Length); Assert.Equal(0, result.Body.Length);
} }
[Fact]
public async Task ReturningTaskFromAction_ProducesNoContentResult()
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.Handler;
// Act
var result = await client.GetAsync("http://localhost/Home/ActionReturningTask");
// Assert
Assert.Equal(204, result.StatusCode);
var body = await result.ReadBodyAsStringAsync();
Assert.Equal("Hello world", body);
}
[Fact] [Fact]
public async Task ActionDescriptors_CreatedOncePerRequest() public async Task ActionDescriptors_CreatedOncePerRequest()
{ {

View File

@ -1,4 +1,8 @@
using Microsoft.AspNet.Mvc; // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
namespace BasicWebSite.Controllers namespace BasicWebSite.Controllers
{ {
@ -18,5 +22,10 @@ namespace BasicWebSite.Controllers
{ {
return new HttpStatusCodeResult(204); return new HttpStatusCodeResult(204);
} }
public async Task ActionReturningTask()
{
await Context.Response.WriteAsync("Hello world");
}
} }
} }