Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Make DefaultApiDescriptionProvider understand ActionResult<T>
Browse files Browse the repository at this point in the history
Fixes #6784
  • Loading branch information
pranavkm committed Sep 8, 2017
1 parent 717f1e6 commit 656f35d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,7 @@ private IList<ApiParameterDescription> GetParameters(ApiParameterContext context
parameter.Source == BindingSource.ModelBinding ||
parameter.Source == BindingSource.Custom)
{
ApiParameterRouteInfo routeInfo;
if (routeParameters.TryGetValue(parameter.Name, out routeInfo))
if (routeParameters.TryGetValue(parameter.Name, out var routeInfo))
{
parameter.RouteInfo = routeInfo;
routeParameters.Remove(parameter.Name);
Expand Down Expand Up @@ -322,8 +321,7 @@ private IReadOnlyList<ApiRequestFormat> GetRequestFormats(
{
foreach (var formatter in _inputFormatters)
{
var requestFormatMetadataProvider = formatter as IApiRequestFormatMetadataProvider;
if (requestFormatMetadataProvider != null)
if (formatter is IApiRequestFormatMetadataProvider requestFormatMetadataProvider)
{
var supportedTypes = requestFormatMetadataProvider.GetSupportedContentTypes(contentType, type);

Expand Down Expand Up @@ -445,7 +443,10 @@ private Type GetDeclaredReturnType(ControllerActionDescriptor action)
}

// Unwrap the type if it's a Task<T>. The Task (non-generic) case was already handled.
var unwrappedType = GetTaskInnerTypeOrNull(declaredReturnType) ?? declaredReturnType;
var unwrappedType = UnwrapGenericType(declaredReturnType, typeof(Task<>));

// UnWrap the type if it's ActionResult<T> or Task<ActionResult<T>>.
unwrappedType = UnwrapGenericType(unwrappedType, typeof(ActionResult<>));

// If the method is declared to return IActionResult or a derived class, that information
// isn't valuable to the formatter.
Expand All @@ -457,13 +458,12 @@ private Type GetDeclaredReturnType(ControllerActionDescriptor action)
{
return unwrappedType;
}
}

private static Type GetTaskInnerTypeOrNull(Type type)
{
var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(Task<>));

return genericType?.GenericTypeArguments[0];
Type UnwrapGenericType(Type type, Type queryType)
{
var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, queryType);
return genericType?.GenericTypeArguments[0] ?? type;
}
}

private Type GetRuntimeReturnType(Type declaredReturnType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,42 @@ public void GetApiDescription_PopulatesResponseType_WithProduct()
Assert.NotNull(responseType.ModelMetadata);
}

[Theory]
[InlineData(nameof(ReturnsActionResultOfProduct))]
[InlineData(nameof(ReturnsTaskOfActionResultOfProduct))]
public void GetApiDescription_PopulatesResponseType_ForActionResultOfT(string methodName)
{
// Arrange
var action = CreateActionDescriptor(methodName);

// Act
var descriptions = GetApiDescriptions(action);

// Assert
var description = Assert.Single(descriptions);
var responseType = Assert.Single(description.SupportedResponseTypes);
Assert.Equal(typeof(Product), responseType.Type);
Assert.NotNull(responseType.ModelMetadata);
}

[Theory]
[InlineData(nameof(ReturnsActionResultOfSequenceOfProducts))]
[InlineData(nameof(ReturnsTaskOfActionResultOfSequenceOfProducts))]
public void GetApiDescription_PopulatesResponseType_ForActionResultOfSequenceOfT(string methodName)
{
// Arrange
var action = CreateActionDescriptor(methodName);

// Act
var descriptions = GetApiDescriptions(action);

// Assert
var description = Assert.Single(descriptions);
var responseType = Assert.Single(description.SupportedResponseTypes);
Assert.Equal(typeof(IEnumerable<Product>), responseType.Type);
Assert.NotNull(responseType.ModelMetadata);
}

[Fact]
public void GetApiDescription_PopulatesResponseType_WithTaskOfProduct()
{
Expand Down Expand Up @@ -1478,6 +1514,14 @@ private Product ReturnsProduct()
return null;
}

private ActionResult<Product> ReturnsActionResultOfProduct() => null;

private ActionResult<IEnumerable<Product>> ReturnsActionResultOfSequenceOfProducts() => null;

private Task<ActionResult<Product>> ReturnsTaskOfActionResultOfProduct() => null;

private Task<ActionResult<IEnumerable<Product>>> ReturnsTaskOfActionResultOfSequenceOfProducts() => null;

private void AcceptsProduct(Product product)
{
}
Expand Down

0 comments on commit 656f35d

Please sign in to comment.