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.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);
|
||||
|
|
@ -322,8 +321,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
{
|
||||
foreach (var formatter in _inputFormatters)
|
||||
{
|
||||
var requestFormatMetadataProvider = formatter as IApiRequestFormatMetadataProvider;
|
||||
if (requestFormatMetadataProvider != null)
|
||||
if (formatter is IApiRequestFormatMetadataProvider requestFormatMetadataProvider)
|
||||
{
|
||||
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.
|
||||
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.
|
||||
|
|
@ -457,13 +458,12 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|||
{
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -394,6 +394,42 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
|||
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()
|
||||
{
|
||||
|
|
@ -1478,6 +1514,14 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
|||
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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -478,6 +478,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("GetProduct", "ApiExplorerWebSite.Product")]
|
||||
[InlineData("GetActionResultProduct", "ApiExplorerWebSite.Product")]
|
||||
[InlineData("GetInt", "System.Int32")]
|
||||
[InlineData("GetTaskOfProduct", "ApiExplorerWebSite.Product")]
|
||||
[InlineData("GetTaskOfInt", "System.Int32")]
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ namespace ApiExplorerWebSite
|
|||
return null;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult<Product> GetActionResultProduct() => null;
|
||||
|
||||
[HttpGet]
|
||||
public int GetInt()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue