Skip to content

Fix ProducesResponseType's Description not being set for Minimal API's when attribute and inferred types aren't an exact match #62695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@
Type returnType,
EndpointMetadataCollection endpointMetadata)
{
System.Diagnostics.Debugger.Break();
var responseType = returnType;

// We support attributes (which implement the IApiResponseMetadataProvider) interface
Expand Down Expand Up @@ -404,15 +405,28 @@
string? matchingDescription = null;
foreach (var metadata in responseMetadataTypes)
{
if (metadata.StatusCode == apiResponseType.StatusCode &&

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Source-Build (Managed))

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: macOS)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS arm64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 408 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L408

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(408,44): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.
metadata.Type == apiResponseType.Type &&
TypesAreCompatible(apiResponseType?.Type, metadata?.Type) &&
metadata.Description is not null)

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Source-Build (Managed))

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: macOS)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS arm64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.

Check failure on line 410 in src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L410

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs(410,21): error CS8602: (NETCORE_ENGINEERING_TELEMETRY=Build) Dereference of a possibly null reference.
{
matchingDescription = metadata.Description;
}
}
return matchingDescription;
}

static bool TypesAreCompatible(Type? apiResponseType, Type? metadaType)
{
// We need to a special check for cases where the inferred type is different than the one specified in attributes.
// For example, an endpoint that defines [ProducesResponseType<IEnumerable<WeatherForecast>>],
// but the endpoint returns weatherForecasts.ToList(). Because List<> is a different type than IEnumerable<>, it would incorrectly return false.
// Currently, we do a "simple" biderectional check to see if the types are assignable to each other.
// This isn't very thorough, but it works for most cases.
// For more information, check the related bug: https://github.com/dotnet/aspnetcore/issues/60518
return apiResponseType == metadaType ||
metadaType?.IsAssignableFrom(apiResponseType) == true ||
apiResponseType?.IsAssignableFrom(metadaType) == true;
}
}

private static ApiResponseType CreateDefaultApiResponseType(Type responseType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,82 @@ public void AddsResponseDescription()
Assert.Equal(expectedBadRequestDescription, badRequestResponseType.Description);
}

[Fact]
public void AddsResponseDescription_WorksWithGenerics()
{
const string expectedOkDescription = "The weather forecast for the next 5 days.";

var apiDescription = GetApiDescription([ProducesResponseType<GenericClass<TimeSpan>>(StatusCodes.Status200OK, Description = "The weather forecast for the next 5 days.")]
() => new GenericClass<TimeSpan> { Value = new TimeSpan() });

var okResponseType = Assert.Single(apiDescription.SupportedResponseTypes);

Assert.Equal(200, okResponseType.StatusCode);
Assert.Equal(typeof(GenericClass<TimeSpan>), okResponseType.Type);
Assert.Equal(typeof(GenericClass<TimeSpan>), okResponseType.ModelMetadata?.ModelType);
Assert.Equal(expectedOkDescription, okResponseType.Description);

var createdOkFormat = Assert.Single(okResponseType.ApiResponseFormats);
Assert.Equal("application/json", createdOkFormat.MediaType);
}

[Fact]
public void AddsResponseDescription_WorksWithGenericsAndTypedResults()
{
const string expectedOkDescription = "The weather forecast for the next 5 days.";

var apiDescription = GetApiDescription([ProducesResponseType<GenericClass<TimeSpan>>(StatusCodes.Status200OK, Description = "The weather forecast for the next 5 days.")]
() => TypedResults.Ok(new GenericClass<TimeSpan> { Value = new TimeSpan() }));

var okResponseType = Assert.Single(apiDescription.SupportedResponseTypes);

Assert.Equal(200, okResponseType.StatusCode);
Assert.Equal(typeof(GenericClass<TimeSpan>), okResponseType.Type);
Assert.Equal(typeof(GenericClass<TimeSpan>), okResponseType.ModelMetadata?.ModelType);
Assert.Equal(expectedOkDescription, okResponseType.Description);

var createdOkFormat = Assert.Single(okResponseType.ApiResponseFormats);
Assert.Equal("application/json", createdOkFormat.MediaType);
}

[Fact]
public void AddsResponseDescription_WorksWithCollections()
{
const string expectedOkDescription = "The weather forecast for the next 5 days.";

var apiDescription = GetApiDescription([ProducesResponseType<IEnumerable<TimeSpan>>(StatusCodes.Status200OK, Description = "The weather forecast for the next 5 days.")]
() => new List<TimeSpan> { new() });

var okResponseType = Assert.Single(apiDescription.SupportedResponseTypes);

Assert.Equal(200, okResponseType.StatusCode);
Assert.Equal(typeof(List<TimeSpan>), okResponseType.Type); // We use List as the inferred type has higher priority than those set by metadata (attributes)
Assert.Equal(typeof(List<TimeSpan>), okResponseType.ModelMetadata?.ModelType);
Assert.Equal(expectedOkDescription, okResponseType.Description);

var createdOkFormat = Assert.Single(okResponseType.ApiResponseFormats);
Assert.Equal("application/json", createdOkFormat.MediaType);
}

[Fact]
public void AddsResponseDescription_WorksWithCollectionsAndTypedResults()
{
const string expectedOkDescription = "The weather forecast for the next 5 days.";

var apiDescription = GetApiDescription([ProducesResponseType<IEnumerable<TimeSpan>>(StatusCodes.Status200OK, Description = "The weather forecast for the next 5 days.")]
() => TypedResults.Ok(new List<TimeSpan> { new() }));

var okResponseType = Assert.Single(apiDescription.SupportedResponseTypes);

Assert.Equal(200, okResponseType.StatusCode);
Assert.Equal(typeof(List<TimeSpan>), okResponseType.Type); // We use List as the inferred type has higher priority than those set by metadata (attributes)
Assert.Equal(typeof(List<TimeSpan>), okResponseType.ModelMetadata?.ModelType);
Assert.Equal(expectedOkDescription, okResponseType.Description);

var createdOkFormat = Assert.Single(okResponseType.ApiResponseFormats);
Assert.Equal("application/json", createdOkFormat.MediaType);
}

[Fact]
public void WithEmptyMethodBody_AddsResponseDescription()
{
Expand Down Expand Up @@ -1813,5 +1889,7 @@ private class TestServiceProvider : IServiceProvider

return null;
}

}
private class GenericClass<TType> { public required TType Value { get; set; } }
}
Loading