Make DefaultApiDescriptionProvider understand ActionResult<T>
Fixes #6784
This commit is contained in:
parent
6bf165f22f
commit
63397653fa
|
|
@ -193,8 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
parameter.Source == BindingSource.ModelBinding ||
|
parameter.Source == BindingSource.ModelBinding ||
|
||||||
parameter.Source == BindingSource.Custom)
|
parameter.Source == BindingSource.Custom)
|
||||||
{
|
{
|
||||||
ApiParameterRouteInfo routeInfo;
|
if (routeParameters.TryGetValue(parameter.Name, out var routeInfo))
|
||||||
if (routeParameters.TryGetValue(parameter.Name, out routeInfo))
|
|
||||||
{
|
{
|
||||||
parameter.RouteInfo = routeInfo;
|
parameter.RouteInfo = routeInfo;
|
||||||
routeParameters.Remove(parameter.Name);
|
routeParameters.Remove(parameter.Name);
|
||||||
|
|
@ -322,8 +321,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
{
|
{
|
||||||
foreach (var formatter in _inputFormatters)
|
foreach (var formatter in _inputFormatters)
|
||||||
{
|
{
|
||||||
var requestFormatMetadataProvider = formatter as IApiRequestFormatMetadataProvider;
|
if (formatter is IApiRequestFormatMetadataProvider requestFormatMetadataProvider)
|
||||||
if (requestFormatMetadataProvider != null)
|
|
||||||
{
|
{
|
||||||
var supportedTypes = requestFormatMetadataProvider.GetSupportedContentTypes(contentType, type);
|
var supportedTypes = requestFormatMetadataProvider.GetSupportedContentTypes(contentType, type);
|
||||||
|
|
||||||
|
|
@ -445,7 +443,10 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap the type if it's a Task<T>. The Task (non-generic) case was already handled.
|
// 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
|
// If the method is declared to return IActionResult or a derived class, that information
|
||||||
// isn't valuable to the formatter.
|
// isn't valuable to the formatter.
|
||||||
|
|
@ -457,13 +458,12 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
{
|
{
|
||||||
return unwrappedType;
|
return unwrappedType;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static Type GetTaskInnerTypeOrNull(Type type)
|
Type UnwrapGenericType(Type type, Type queryType)
|
||||||
{
|
{
|
||||||
var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(Task<>));
|
var genericType = ClosedGenericMatcher.ExtractGenericInterface(type, queryType);
|
||||||
|
return genericType?.GenericTypeArguments[0] ?? type;
|
||||||
return genericType?.GenericTypeArguments[0];
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type GetRuntimeReturnType(Type declaredReturnType)
|
private Type GetRuntimeReturnType(Type declaredReturnType)
|
||||||
|
|
|
||||||
|
|
@ -394,6 +394,42 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
||||||
Assert.NotNull(responseType.ModelMetadata);
|
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]
|
[Fact]
|
||||||
public void GetApiDescription_PopulatesResponseType_WithTaskOfProduct()
|
public void GetApiDescription_PopulatesResponseType_WithTaskOfProduct()
|
||||||
{
|
{
|
||||||
|
|
@ -1478,6 +1514,14 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
||||||
return null;
|
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)
|
private void AcceptsProduct(Product product)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -478,6 +478,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("GetProduct", "ApiExplorerWebSite.Product")]
|
[InlineData("GetProduct", "ApiExplorerWebSite.Product")]
|
||||||
|
[InlineData("GetActionResultProduct", "ApiExplorerWebSite.Product")]
|
||||||
[InlineData("GetInt", "System.Int32")]
|
[InlineData("GetInt", "System.Int32")]
|
||||||
[InlineData("GetTaskOfProduct", "ApiExplorerWebSite.Product")]
|
[InlineData("GetTaskOfProduct", "ApiExplorerWebSite.Product")]
|
||||||
[InlineData("GetTaskOfInt", "System.Int32")]
|
[InlineData("GetTaskOfInt", "System.Int32")]
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ namespace ApiExplorerWebSite
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<Product> GetActionResultProduct() => null;
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public int GetInt()
|
public int GetInt()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue