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
var actionResult = actionReturnValue as IActionResult;
if (actionResult != null)
{
return actionResult;
}
if (actionReturnValue == null && typeof(IActionResult).IsAssignableFrom(declaredReturnType))
{
throw new InvalidOperationException(
Resources.FormatActionResult_ActionReturnValueCannotBeNull(declaredReturnType));
}
if (declaredReturnType == typeof(void))
if (declaredReturnType == typeof(void) ||
declaredReturnType == typeof(Task))
{
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);
}
@ -275,7 +277,7 @@ namespace Microsoft.AspNet.Mvc
{
var parameterType = parameter.BodyParameterInfo.ParameterType;
var modelMetadata = metadataProvider.GetMetadataForType(
modelAccessor: null,
modelAccessor: null,
modelType: parameterType);
var providerContext = new InputFormatterProviderContext(
actionBindingContext.ActionContext.HttpContext,
@ -295,7 +297,7 @@ namespace Microsoft.AspNet.Mvc
{
var parameterType = parameter.ParameterBindingInfo.ParameterType;
var modelMetadata = metadataProvider.GetMetadataForType(
modelAccessor: null,
modelAccessor: null,
modelType: parameterType);
var modelBindingContext = new ModelBindingContext
@ -400,12 +402,8 @@ namespace Microsoft.AspNet.Mvc
_actionContext.Controller,
_actionExecutingContext.ActionArguments);
var underlyingReturnType =
TypeHelper.GetTaskInnerTypeOrNull(actionMethodInfo.ReturnType) ??
actionMethodInfo.ReturnType;
var actionResult = CreateActionResult(
underlyingReturnType,
actionMethodInfo.ReturnType,
actionReturnValue);
return actionResult;
}
@ -459,8 +457,8 @@ namespace Microsoft.AspNet.Mvc
{
// Short-circuited by not calling next
_resultExecutedContext = new ResultExecutedContext(
_resultExecutingContext,
_filters,
_resultExecutingContext,
_filters,
_resultExecutingContext.Result)
{
Canceled = true,
@ -470,8 +468,8 @@ namespace Microsoft.AspNet.Mvc
{
// Short-circuited by setting Cancel == true
_resultExecutedContext = new ResultExecutedContext(
_resultExecutingContext,
_filters,
_resultExecutingContext,
_filters,
_resultExecutingContext.Result)
{
Canceled = true,
@ -486,8 +484,8 @@ namespace Microsoft.AspNet.Mvc
{
// Short-circuited by setting Cancel == true
_resultExecutedContext = new ResultExecutedContext(
_resultExecutingContext,
_filters,
_resultExecutingContext,
_filters,
_resultExecutingContext.Result)
{
Canceled = true,
@ -504,16 +502,16 @@ namespace Microsoft.AspNet.Mvc
Contract.Assert(_resultExecutedContext == null);
_resultExecutedContext = new ResultExecutedContext(
_resultExecutingContext,
_filters,
_resultExecutingContext,
_filters,
_resultExecutingContext.Result);
}
}
catch (Exception exception)
{
_resultExecutedContext = new ResultExecutedContext(
_resultExecutingContext,
_filters,
_resultExecutingContext,
_filters,
_resultExecutingContext.Result)
{
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)

View File

@ -29,22 +29,6 @@ namespace Microsoft.AspNet.Mvc
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]
public async Task InvokeAction_DoesNotInvokeExceptionFilter_WhenActionDoesNotThrow()
{
@ -1241,28 +1225,44 @@ namespace Microsoft.AspNet.Mvc
}
[Theory]
[InlineData(typeof(void), typeof(NoContentResult))]
[InlineData(typeof(string), typeof(ObjectContentResult))]
public void CreateActionResult_Types_ReturnsAppropriateResults(Type type, Type returnType)
[InlineData(typeof(void))]
[InlineData(typeof(Task))]
public void CreateActionResult_Types_ReturnsNoContentResultForTaskAndVoidReturnTypes(Type type)
{
// Arrange & Act
var result = ReflectedActionInvoker.CreateActionResult(type, null).GetType();
// 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]
[MemberData("CreateActionResultData")]
public void CreateActionResult_ReturnsObjectContentResult(object input)
[MemberData("CreateActionResult_ReturnsObjectContentResultData")]
public void CreateActionResult_ReturnsObjectContentResult(Type type, object input)
{
// Arrange & Act
var actualResult = ReflectedActionInvoker.CreateActionResult(input.GetType(), input)
as ObjectContentResult;
var actualResult = ReflectedActionInvoker.CreateActionResult(type, input);
// Assert
Assert.NotNull(actualResult);
Assert.Equal(input, actualResult.Value);
var contentResult = Assert.IsType<ObjectContentResult>(actualResult);
Assert.Same(input, contentResult.Value);
}
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);
}
[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]
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
{
@ -7,7 +11,7 @@ namespace BasicWebSite.Controllers
public IActionResult Index()
{
return View();
}
}
public IActionResult PlainView()
{
@ -18,5 +22,10 @@ namespace BasicWebSite.Controllers
{
return new HttpStatusCodeResult(204);
}
public async Task ActionReturningTask()
{
await Context.Response.WriteAsync("Hello world");
}
}
}